127 lines
11 KiB
Markdown
127 lines
11 KiB
Markdown
# Coding Conventions
|
|
|
|
**Analysis Date:** 2026-04-20
|
|
|
|
## Naming Patterns
|
|
|
|
**Files:**
|
|
- Use Next.js App Router filenames in `src/app`, including `page.tsx`, `layout.tsx`, and `route.ts`, as seen in `src/app/[locale]/page.tsx`, `src/app/[locale]/layout.tsx`, `src/app/api/projects/route.ts`, and `src/app/api/webhook/signals/route.ts`.
|
|
- Use `PascalCase.tsx` for reusable components in `src/components`, for example `src/components/search/HomeSearchBar.tsx`, `src/components/project/ProjectDetail.tsx`, and `src/components/signals/SignalFeedClient.tsx`.
|
|
- Use lower-case or kebab-case utility filenames in `src/lib`, for example `src/lib/auth.ts`, `src/lib/cache.ts`, `src/lib/signal-hotness.ts`, `src/lib/prisma-url.ts`, and `src/lib/tag-taxonomy.ts`.
|
|
- Keep tests co-located and named `*.test.ts`, for example `src/lib/auth.test.ts`, `src/app/api/tags/route.test.ts`, and `src/app/api/tags/reset-projects/route.test.ts`.
|
|
- Treat `src/hooks` as a mixed server query layer plus client hooks. `src/hooks/useProjects.ts` and `src/hooks/useHome.ts` are not React hooks despite the `use*` prefix.
|
|
|
|
**Functions:**
|
|
- Use `camelCase` for helpers and query functions, such as `isValidApiKey` in `src/lib/auth.ts`, `normalizeProjectSort` in `src/hooks/useProjects.ts`, `parseSlugList` in `src/app/api/projects/route.ts`, and `getTimestamp` in `src/app/api/search/ai/route.ts`.
|
|
- Reserve `PascalCase` for React components, prop interfaces, and domain error classes, such as `ProjectDetail` in `src/components/project/ProjectDetail.tsx` and `TagMaintenanceApiError` in `src/app/api/tags/maintenance/service.ts`.
|
|
- Export route handlers as uppercase HTTP verbs from `src/app/api/**/route.ts`, for example `GET` in `src/app/api/tags/route.ts` and `POST` in `src/app/api/webhook/signals/route.ts`.
|
|
|
|
**Variables:**
|
|
- Use `UPPER_SNAKE_CASE` for configuration constants and env-backed settings, such as `N8N_WEBHOOK_URL` in `src/app/api/search/ai/route.ts`, `TAGS_CACHE_REVALIDATE_SECONDS` in `src/app/api/tags/route.ts`, `DB_RETRY_DELAYS_MS` in `src/hooks/useProjects.ts`, and `ENV_KEYS` in `src/lib/prisma-url.test.ts`.
|
|
- Use descriptive names for parsed and normalized input, such as `validatedQuery` in `src/app/api/projects/route.ts`, `validationResult` in `src/app/api/webhook/signals/route.ts`, and `normalizedTagSlugs` in `src/hooks/useProjects.ts`.
|
|
|
|
**Types:**
|
|
- Prefer `type` aliases for Prisma payloads and request payload shapes, such as `ProjectWithFlatTags` in `src/hooks/useProjects.ts`, `SignalWebhookPayload` in `src/lib/validations.ts`, and `ResetProjectsRouteTxMock` in `src/app/api/tags/reset-projects/route.test.ts`.
|
|
- Prefer `interface` for React props, such as `HomeSearchBarProps` in `src/components/search/HomeSearchBar.tsx` and `ProjectDetailProps` in `src/components/project/ProjectDetail.tsx`.
|
|
|
|
## Code Style
|
|
|
|
**Formatting:**
|
|
- Follow `.prettierrc.json`: 2-space indentation, semicolons, double quotes, trailing commas `es5`, and `printWidth` 100.
|
|
- `AGENTS.md` treats Prettier as authoritative and `pnpm` as the required package manager.
|
|
- The codebase currently has mixed formatting. Files such as `src/lib/validations.ts`, `src/lib/prisma-url.ts`, and `src/app/api/tags/route.ts` match the configured double-quote style, while `src/lib/auth.ts`, `src/app/api/projects/route.ts`, and `src/app/api/search/ai/route.ts` still use single quotes and omit semicolons.
|
|
- For new files, follow `.prettierrc.json`. When editing an existing file with a different quote style, either preserve the local file style for a surgical change or reformat the full file consistently.
|
|
|
|
**Linting:**
|
|
- `.eslintrc.json` extends `next/core-web-vitals` and `prettier`.
|
|
- Only `console.warn` and `console.error` are explicitly allowed by `no-console`. This matches the logging used in `src/app/api/tags/route.ts`, `src/app/api/search/ai/route.ts`, and `src/app/api/webhook/signals/route.ts`.
|
|
- Current verification on 2026-04-20: `pnpm lint` passed with no warnings or errors.
|
|
|
|
## Import Organization
|
|
|
|
**Order:**
|
|
1. Framework and platform imports first, such as `next/server`, `next/cache`, `zod`, `crypto`, `fs`, or `@prisma/client`, as seen in `src/app/api/webhook/signals/route.ts` and `src/lib/prisma-url.ts`.
|
|
2. Internal alias imports from `@/` next, such as `@/lib/prisma`, `@/lib/validations`, and `@/hooks/useProjects`.
|
|
3. Relative imports last, such as `./service` in `src/app/api/tags/maintenance/route.ts` and `./MarkdownContent` in `src/components/project/ProjectDetail.tsx`.
|
|
|
|
**Path Aliases:**
|
|
- Use the `@/*` alias defined in `tsconfig.json` and mirrored in `vitest.config.ts`.
|
|
- Prefer `@/` imports for anything under `src`, as seen throughout `src/app/api/tags/route.ts`, `src/app/api/tags/reset-projects/route.ts`, and `src/hooks/useProjects.ts`.
|
|
|
|
## API Validation and Auth
|
|
|
|
**Validation:**
|
|
- Put shared Zod schemas in `src/lib/validations.ts`. Current examples include `ProjectInputSchema`, `SignalWebhookPayloadSchema`, `SignalQuerySchema`, `TagMaintenanceRequestSchema`, and `ProjectTagResetRequestSchema`.
|
|
- Define route-local schemas only when the contract is route-specific, such as `ProjectsQuerySchema` in `src/app/api/projects/route.ts` and `N8NSearchResponseSchema` plus `AISearchRequestSchema` in `src/app/api/search/ai/route.ts`.
|
|
- Use `.safeParse()` when the route should return a custom `400` response without exceptions, as in `src/app/api/tags/maintenance/route.ts`, `src/app/api/tags/reset-projects/route.ts`, and `src/app/api/webhook/signals/route.ts`.
|
|
- Use `.parse()` when the route already catches `ZodError`, as in `src/app/api/projects/route.ts`, `src/app/api/signals/route.ts`, and `src/app/api/search/ai/route.ts`.
|
|
- Use `z.coerce.number()` for query-string pagination and limits, as in `src/lib/validations.ts` and `src/app/api/projects/route.ts`.
|
|
- Use `.superRefine()` for cross-record constraints such as duplicate tag IDs and self-merge prevention, as in `TagMergeSchema`, `TagMaintenanceRequestSchema`, and `ProjectTagResetRequestSchema` in `src/lib/validations.ts`.
|
|
|
|
**Webhook Auth:**
|
|
- Use the timing-safe `isValidApiKey(...)` helper from `src/lib/auth.ts` for internal mutation and webhook routes.
|
|
- Read the expected secret from `process.env.WEBHOOK_API_KEY` unless a test passes an explicit override.
|
|
- Validate the API key format at schema level first, then authenticate with `isValidApiKey(...)`, as done in `src/app/api/tags/maintenance/route.ts`, `src/app/api/tags/reset-projects/route.ts`, and `src/app/api/webhook/signals/route.ts`.
|
|
- Return `401` with a JSON body containing `success: false`, `error: "Unauthorized"`, and a `details` array on auth failure.
|
|
|
|
## Data Access
|
|
|
|
**Prisma:**
|
|
- Use the shared Prisma client from `src/lib/prisma.ts`.
|
|
- Keep read-heavy query composition in `src/hooks/useProjects.ts` and `src/hooks/useHome.ts`.
|
|
- Keep write workflows transactional with `prisma.$transaction(...)`, as in `src/app/api/tags/maintenance/route.ts` and `src/app/api/tags/reset-projects/route.ts`.
|
|
- Use explicit `include` and `select` clauses rather than broad model reads, as seen throughout `src/hooks/useProjects.ts` and `src/app/api/webhook/signals/route.ts`.
|
|
|
|
**Caching and Degrade Patterns:**
|
|
- Use `unstable_cache` for repeatable server reads, as in `src/app/api/tags/route.ts` and `src/hooks/useProjects.ts`.
|
|
- Route cached reads through `runWithCacheFallback` from `src/lib/cache.ts`.
|
|
- Use retry helpers for transient DB reads, as in `withDbRetry(...)` and `isTransientDbError(...)` in `src/hooks/useProjects.ts`.
|
|
- Degrade on schema drift where the route can still succeed, as in `src/app/api/webhook/signals/route.ts` falling back when hotness columns are unavailable.
|
|
|
|
## Error Handling
|
|
|
|
**Patterns:**
|
|
- Wrap route handlers in `try/catch` and return JSON through `NextResponse.json(...)`, as seen in `src/app/api/projects/route.ts`, `src/app/api/search/ai/route.ts`, and `src/app/api/webhook/signals/route.ts`.
|
|
- Return `400` for schema and query validation failures, `401` for invalid webhook keys, and `500` for unexpected failures.
|
|
- Use route-local or domain-specific error classes when mutation logic needs structured status and details, as in `TagMaintenanceApiError` from `src/app/api/tags/maintenance/service.ts`.
|
|
- Keep JSON error payloads stable enough for automation. Current routes usually emit `success`, `error`, `details`, and sometimes `message`, but the exact shape is not yet fully standardized across `src/app/api/**/route.ts`.
|
|
|
|
**Logging:**
|
|
- Use `console.error` for failures and `console.warn` for operational summaries or degraded behavior, matching `.eslintrc.json` and examples in `src/app/api/tags/route.ts`, `src/hooks/useProjects.ts`, and `src/app/api/webhook/signals/route.ts`.
|
|
- Avoid `console.log` in production code because lint warns on it.
|
|
|
|
## Comments
|
|
|
|
**When to Comment:**
|
|
- Keep comments sparse and intent-focused. Existing comments mostly explain numbered handler steps, fallback rationale, or bilingual product context, as in `src/app/api/tags/maintenance/route.ts`, `src/hooks/useProjects.ts`, and `src/app/api/search/ai/route.ts`.
|
|
- Use short JSDoc only where security or contract semantics matter, such as the timing-safe explanation above `isValidApiKey(...)` in `src/lib/auth.ts`.
|
|
|
|
## Function Design
|
|
|
|
**Size:**
|
|
- The repository tolerates large query and route modules. Current examples include `src/hooks/useProjects.ts`, `src/app/api/signals/route.ts`, and `src/app/api/tags/reset-projects/route.ts`.
|
|
- Keep helper functions close to the route or query layer before extracting a new module. Examples include `parseSlugList(...)` in `src/app/api/projects/route.ts`, `normalizeSlugList(...)` in `src/app/api/search/ai/route.ts`, and `toSectionsJson(...)` in `src/app/api/webhook/signals/route.ts`.
|
|
|
|
**Parameters:**
|
|
- Prefer a single typed options object for non-trivial query helpers, as in `getProjects(...)` from `src/hooks/useProjects.ts`.
|
|
- For route handlers, parse from `request.nextUrl.searchParams` or `await request.json()` once, then normalize into a validated object before passing deeper.
|
|
|
|
**Return Values:**
|
|
- Return plain serializable objects from API routes and query helpers.
|
|
- Flatten Prisma relation shapes before returning UI data, as done by `getProjects(...)` and `getProjectsByIds(...)` in `src/hooks/useProjects.ts`.
|
|
|
|
## Module Design
|
|
|
|
**Exports:**
|
|
- Prefer named exports across shared modules and components. No barrel files were detected under `src`.
|
|
- Use default exports mainly for App Router pages and layouts under `src/app`.
|
|
|
|
**Separation of Concerns:**
|
|
- Keep Prisma-backed reads in `src/hooks/useProjects.ts` and `src/hooks/useHome.ts`.
|
|
- Keep cross-cutting utilities in `src/lib`, such as `src/lib/auth.ts`, `src/lib/cache.ts`, `src/lib/prisma.ts`, and `src/lib/validations.ts`.
|
|
- Keep mutation business logic in a local service file when the route would otherwise mix transport and domain rules. `src/app/api/tags/maintenance/route.ts` plus `src/app/api/tags/maintenance/service.ts` is the clearest existing pattern.
|
|
|
|
---
|
|
|
|
*Convention analysis: 2026-04-20*
|