This document provides coding guidelines and conventions for AI agents working on this codebase.
GitHub Proxy Server is a Node.js TypeScript application that proxies GitHub API requests with automatic token management and rate limiting. It uses Express for the server, Vitest for testing, and Biome for linting/formatting.
npm run dev # Start dev server with auto-reload (tsx watch)
npm run dev-no-reload # Start dev server without reloadnpm run build # Clean dist/ and build with tsup (ESM, minified, sourcemap)
npm start # Run built CLI from dist/cli.jsnpm test # Run all tests (vitest run)
npm run test:watch # Run tests in watch mode
npm run test:coverage # Run tests with coverage report
# Run a single test file
npx vitest run src/cli.spec.ts
# Run tests matching a pattern
npx vitest run --grep "should create a CLI program"npm run lint # Check code with Biome
npm run lint:fix # Auto-fix linting issues
npm run format # Format code with BiomePre-commit: Runs npm run lint and npm run build
Commit-msg: Validates commit messages using commitlint (conventional commits)
npm run commit # Interactive conventional commit (commitizen)
npm run release # Create version tag with standard-version
npm run np # Publish package (no-publish mode)- Source files:
src/**/*.ts - Test files:
src/**/*.spec.ts(co-located with source) - Built files:
dist/(git-ignored) - Entry point:
src/cli.ts
Imports must follow this order with blank lines between groups:
- Node.js built-in modules (
:NODE:) - Blank line
- Third-party packages (
:PACKAGE:) and aliases (:ALIAS:) - Blank line
- Local imports
Example:
import EventEmitter from 'node:events';
import { pathToFileURL } from 'node:url';
import chalk from 'chalk';
import { Command, Option } from 'commander';
import consola from 'consola';
import { createProxyServer } from './server.js';- Use ES modules (not CommonJS)
- Use
.jsextensions in imports (TypeScript ESM requirement) - Use
node:protocol for Node.js built-ins (e.g.,node:fs,node:path) - JSON imports use
with { type: 'json' }syntax - Prefer named imports over default imports
- Indentation: 2 spaces
- Line width: 100 characters
- Line ending: LF
- Quotes: Single quotes for JS/TS, double quotes for JSX
- Semicolons: Always required
- Trailing commas: Never
- Arrow parentheses: Always (e.g.,
(x) => x + 1) - Bracket spacing: Enabled (
{ foo }not{foo})
- Target: ESNext
- Module: NodeNext with NodeNext resolution
- Strict mode: Enabled
- Always use explicit types for function parameters and return values
- Avoid
any- useunknownor proper types - Use
typefor object shapes,interfacefor extendable contracts - Enable
strict: trueand all strict type-checking options
- Files: kebab-case (e.g.,
proxy-client.ts,proxy-client.spec.ts) - Classes: PascalCase (e.g.,
ProxyClient,ProxyWorker) - Functions/variables: camelCase (e.g.,
createProxyServer,statusFormatter) - Constants: UPPER_SNAKE_CASE for true constants, camelCase for others
- Type aliases: PascalCase (e.g.,
ProxyClientOptions,CliOpts) - Private class members: Prefix with underscore (e.g.,
_queued,_running)
- Use typed errors with specific error codes
- Example:
const error = new Error('ETIMEDOUT') as Error & { code: string }; error.code = 'ETIMEDOUT'; - Always handle promise rejections
- Use try-catch for async operations
- Emit errors via EventEmitter when appropriate
- Use
consolafor CLI logging (info, success, warn, error) - Use
pinofor HTTP request logging (when DEBUG=true) - No
console.logallowed (enforced by Biome:noConsole: "error") - Format data with custom formatters (see
logTransformin server.ts)
- Test framework: Vitest
- Test files: Co-located with source as
*.spec.ts - Use
describeblocks for grouping related tests - Use
testoritfor individual test cases - Test file structure: imports → test suites → test cases
- Use
beforeEach/afterEachfor setup/teardown - Mock external dependencies with
vitest.mock()ornockfor HTTP
- Use JSDoc comments for public functions and classes
- Example:
/**
* Proxy an incoming HTTP request to the target server
* @param req - Incoming request from Express
* @param res - Outgoing response to Express client
* @param options - Additional options for request modification
*/- Add author attribution:
/* Author: Hudson S. Borges */at top of new files - Use inline comments sparingly, prefer self-documenting code
- Prefer async/await over raw promises
- Always handle errors in async functions
- Use AbortController for timeout handling
- Example timeout pattern:
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const result = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
return result;
} catch (error) {
clearTimeout(timeoutId);
throw error;
}- Disable
x-powered-byheader - Use compression middleware with configurable level
- Use basic auth for protected endpoints (exclude
/status) - Support express route patterns like
{/*path}for wildcards
- Extend EventEmitter for custom classes
- Emit typed events:
log,warn,error - Set
defaultMaxListenersto avoid warnings in high-concurrency scenarios
- Support CLI flags and environment variables
- Use
commanderfor CLI parsing with.env()for env var binding - Validate required options (e.g., tokens must be 40 characters)
- Import extensions are required (
.jsnot.tsin imports) - Tests exclude pattern in tsconfig:
"exclude": ["node_modules", "**/*.spec.ts"] - Vitest includes tests:
include: ['src/**/*.spec.ts'] - Build output is ESM only (
"type": "module"in package.json) - Node.js version requirement:
>=24 - Husky hooks will fail the commit if lint or build fail