docs: map existing codebase
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
# Coding Conventions
|
||||
|
||||
**Analysis Date:** 2026-04-18
|
||||
|
||||
## Naming Patterns
|
||||
|
||||
**Files:**
|
||||
- Use Next.js route filenames in `src/app`: `page.tsx`, `layout.tsx`, `route.ts`, `not-found.tsx`, `robots.ts`, and `sitemap.ts` as seen in `src/app/[locale]/page.tsx`, `src/app/[locale]/layout.tsx`, `src/app/api/projects/route.ts`, and `src/app/[locale]/not-found.tsx`.
|
||||
- Use `PascalCase.tsx` for reusable components in `src/components`, for example `src/components/project/ProjectCard.tsx`, `src/components/project/TagFilterPanel.tsx`, and `src/components/signals/SignalFeedClient.tsx`.
|
||||
- Use lower-case utility filenames in `src/lib`, for example `src/lib/auth.ts`, `src/lib/cache.ts`, `src/lib/signal-hotness.ts`, and `src/lib/validations.ts`.
|
||||
- `src/hooks` is not limited to React hooks. `src/hooks/useProjects.ts` and `src/hooks/useHome.ts` export server-side data access and aggregation functions, not hook APIs. Extend those files only when adding the same kind of server query layer.
|
||||
|
||||
**Functions:**
|
||||
- Use `camelCase` for functions and helpers: `isValidApiKey` in `src/lib/auth.ts`, `normalizeProjectSort` in `src/hooks/useProjects.ts`, `runWithCacheFallback` in `src/lib/cache.ts`, and `collectSelectedTagSlugs` in `src/app/api/tags/reset-projects/route.ts`.
|
||||
- Use `PascalCase` only for React components and error classes: `ProjectCard` in `src/components/project/ProjectCard.tsx`, `ProjectsResultsClient` in `src/app/[locale]/projects/ProjectsResultsClient.tsx`, and `TagMaintenanceApiError` in `src/app/api/tags/maintenance/service.ts`.
|
||||
- Use `GET` and `POST` named exports for route handlers in `src/app/api/**/route.ts`.
|
||||
|
||||
**Variables:**
|
||||
- Use `UPPER_SNAKE_CASE` for constants and configuration knobs, for example `DB_RETRY_DELAYS_MS` in `src/hooks/useProjects.ts`, `DEFAULT_RESET_CATEGORIES` in `src/app/api/tags/reset-projects/route.ts`, `TAGS_CACHE_REVALIDATE_SECONDS` in `src/app/api/tags/route.ts`, and `N8N_WEBHOOK_URL` in `src/app/api/search/ai/route.ts`.
|
||||
- Use descriptive typed local variables for parsed or normalized input, such as `validatedQuery` in `src/app/api/projects/route.ts`, `normalizedTagSlugs` in `src/hooks/useProjects.ts`, and `validationResult` in `src/app/api/webhook/signals/route.ts`.
|
||||
|
||||
**Types:**
|
||||
- Prefer `type` aliases for data shapes and Prisma payloads, for example `ProjectWithFlatTags` in `src/hooks/useProjects.ts`, `HomePageData` in `src/hooks/useHome.ts`, and `ResetResultItem` in `src/app/api/tags/reset-projects/route.ts`.
|
||||
- Use `interface` for component props, for example `ProjectCardProps` in `src/components/project/ProjectCard.tsx`, `ProjectsPageClientProps` in `src/app/[locale]/projects/ProjectsPageClient.tsx`, and `SignalFeedClientProps` in `src/components/signals/SignalFeedClient.tsx`.
|
||||
|
||||
## Code Style
|
||||
|
||||
**Formatting:**
|
||||
- Prettier is configured in `.prettierrc.json` for 2-space indentation, semicolons, double quotes, trailing commas set to `es5`, and `printWidth` 100.
|
||||
- The repository is not uniformly formatted to that config. Files such as `src/lib/validations.ts`, `src/hooks/useProjects.ts`, and `src/app/api/tags/maintenance/route.ts` match the configured double-quote and semicolon style, while `src/app/api/projects/route.ts`, `src/app/api/search/ai/route.ts`, `src/lib/auth.ts`, and `src/components/project/ProjectCard.tsx` use single quotes and omit semicolons.
|
||||
- For new files, follow `.prettierrc.json`. When editing existing files, preserve the file-local style unless the whole file is reformatted.
|
||||
|
||||
**Linting:**
|
||||
- ESLint extends `next/core-web-vitals` and `prettier` in `.eslintrc.json`.
|
||||
- `console.warn` and `console.error` are allowed; other `console` calls are warned by `no-console`.
|
||||
- Quality checks on 2026-04-18: `pnpm lint` passed with `✔ No ESLint warnings or errors`.
|
||||
|
||||
## Import Organization
|
||||
|
||||
**Order:**
|
||||
1. Framework and platform imports first, for example `next/server`, `next/cache`, `zod`, `@prisma/client`, or `react`.
|
||||
2. Internal alias imports from `@/`, for example `@/lib/prisma`, `@/hooks/useProjects`, and `@/lib/validations`.
|
||||
3. Relative imports last, for example `./service` in `src/app/api/tags/maintenance/route.ts`.
|
||||
|
||||
**Path Aliases:**
|
||||
- Use the `@/*` alias from `tsconfig.json` and `vitest.config.ts` for internal imports.
|
||||
- Prefer `@/` over deep relative paths across `src`, for example `@/lib/prisma` in `src/app/api/tags/route.ts` and `@/lib/tag-taxonomy` in `src/hooks/useProjects.ts`.
|
||||
|
||||
## Component and Module Design
|
||||
|
||||
**React Components:**
|
||||
- Default-export only route-level pages and layouts, for example `src/app/[locale]/page.tsx`, `src/app/[locale]/projects/page.tsx`, and `src/app/layout.tsx`.
|
||||
- Use named exports for reusable components, for example `ProjectCard` in `src/components/project/ProjectCard.tsx`, `HomeRankings` in `src/components/home/HomeRankings.tsx`, and `AnnouncementBar` in `src/components/layout/AnnouncementBar.tsx`.
|
||||
- Mark interactive components with `'use client'`, as seen in `src/app/[locale]/projects/ProjectsResultsClient.tsx`, `src/app/[locale]/projects/ProjectsPageClient.tsx`, `src/components/project/TagFilterPanel.tsx`, and `src/components/signals/SignalFeedClient.tsx`.
|
||||
- Keep server components async and free of client hooks, as seen in `src/app/[locale]/projects/[id]/page.tsx`, `src/app/[locale]/signals/page.tsx`, and `src/components/project/ProjectSidebar.tsx`.
|
||||
- Type component props explicitly with a local `interface ...Props`.
|
||||
|
||||
**Service and Query Modules:**
|
||||
- Centralize Prisma-backed read logic in `src/hooks/useProjects.ts` and `src/hooks/useHome.ts`. Despite the directory name, these modules act as query services for routes and server components.
|
||||
- Keep route handlers thin where possible and delegate business rules to local services for mutations. The clearest example is `src/app/api/tags/maintenance/route.ts` delegating to `src/app/api/tags/maintenance/service.ts`.
|
||||
- Reuse shared utility modules in `src/lib` for cross-cutting concerns: `src/lib/auth.ts`, `src/lib/cache.ts`, `src/lib/prisma.ts`, `src/lib/signal-hotness.ts`, `src/lib/slug.ts`, and `src/lib/tag-taxonomy.ts`.
|
||||
|
||||
**Exports:**
|
||||
- Prefer named exports across shared modules. No barrel files were detected under `src` on 2026-04-18.
|
||||
|
||||
## Validation
|
||||
|
||||
**Schema Placement:**
|
||||
- Put broadly shared Zod schemas in `src/lib/validations.ts`. Examples include `ProjectInputSchema`, `SignalWebhookPayloadSchema`, `SignalQuerySchema`, and `TagMaintenanceRequestSchema`.
|
||||
- Define route-local Zod schemas only when the contract is tightly coupled to a single endpoint, as in `ProjectsQuerySchema` in `src/app/api/projects/route.ts` and `N8NSearchResponseSchema` in `src/app/api/search/ai/route.ts`.
|
||||
|
||||
**Validation Flow:**
|
||||
- Use `.safeParse()` when the route needs to return a custom `400` payload 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 has a `ZodError` catch branch, as in `src/app/api/projects/route.ts`, `src/app/api/search/ai/route.ts`, and `src/app/api/signals/route.ts`.
|
||||
- Use `z.coerce` for query-string number parsing in shared schemas, as in `SignalQuerySchema` and `ProjectQuerySchema` in `src/lib/validations.ts`.
|
||||
- Add cross-field validation with `.superRefine()` for multi-item or relation rules, as in `TagMergeSchema` and `TagMaintenanceRequestSchema` in `src/lib/validations.ts`.
|
||||
|
||||
## Data Access
|
||||
|
||||
**Prisma:**
|
||||
- Use the singleton Prisma client from `src/lib/prisma.ts`.
|
||||
- Define Prisma payload types close to the query layer with `Prisma.*GetPayload`, as in `ProjectWithTagsAndLinks` and `TagWithProjectCount` in `src/hooks/useProjects.ts`.
|
||||
- Prefer explicit `include` and `select` clauses to control payload shape, as seen throughout `src/hooks/useProjects.ts`, `src/hooks/useHome.ts`, `src/app/api/signals/route.ts`, and `src/app/api/tags/reset-projects/route.ts`.
|
||||
- Use `prisma.$transaction(...)` for write paths that change multiple tables, as in `src/app/api/tags/maintenance/route.ts` and `src/app/api/tags/reset-projects/route.ts`.
|
||||
|
||||
**Caching and Fallbacks:**
|
||||
- Wrap cacheable server reads with `unstable_cache`, using stable key arrays and `revalidate` windows, as in `src/app/api/tags/route.ts`, `src/hooks/useProjects.ts`, and `src/hooks/useHome.ts`.
|
||||
- Route cached reads through `runWithCacheFallback` from `src/lib/cache.ts` so execution can fall back when `unstable_cache` is unavailable.
|
||||
- Cache fetcher functions keyed by input when the function signature varies, as in `topTagsCache` and `fixedProjectTypeFilterCache` in `src/hooks/useProjects.ts`.
|
||||
|
||||
**Retry and Degrade Patterns:**
|
||||
- Use retry wrappers for transient DB issues on read paths. `withDbRetry` and `isTransientDbError` in `src/hooks/useProjects.ts` are the current pattern.
|
||||
- Degrade to safe defaults on non-critical homepage and filter data instead of failing the whole page, as in `safeQuery` in `src/hooks/useHome.ts` and `console.error` fallback branches in `src/hooks/useProjects.ts`.
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Patterns:**
|
||||
- Wrap route handlers in `try/catch` and return JSON error payloads through `NextResponse.json`, as in `src/app/api/projects/route.ts`, `src/app/api/tags/route.ts`, `src/app/api/search/ai/route.ts`, and `src/app/api/webhook/signals/route.ts`.
|
||||
- Return `400` for schema and cursor validation failures, `401` for invalid API keys, `409` for tag conflicts inside service code, and `500` for unexpected errors.
|
||||
- Use domain-specific error classes when mutation services need to communicate status and details back to routes. `TagMaintenanceApiError` in `src/app/api/tags/maintenance/service.ts` is the established pattern.
|
||||
- Include machine-readable `success`, `error`, `details`, and sometimes `message` fields in JSON responses. The exact shape varies by route and is not fully normalized.
|
||||
|
||||
**Logging:**
|
||||
- Use `console.error` for failures and `console.warn` for degraded or summary logging, for example `src/hooks/useHome.ts`, `src/hooks/useProjects.ts`, `src/app/api/signals/route.ts`, and `src/app/api/webhook/signals/route.ts`.
|
||||
- Error-path tests currently allow log output to stderr, as verified by `pnpm test` on 2026-04-18 from `src/app/api/tags/route.test.ts`.
|
||||
|
||||
## Comments
|
||||
|
||||
**When to Comment:**
|
||||
- Comments are sparse and usually explain non-obvious intent, numbered route steps, or bilingual product context.
|
||||
- English and Chinese comments coexist. Examples include the numbered route comments in `src/app/api/tags/maintenance/route.ts`, Chinese comments in `src/hooks/useProjects.ts`, and mixed bilingual commentary in `src/components/project/ProjectCard.tsx`.
|
||||
- Prefer comments only where intent is not obvious from code.
|
||||
|
||||
**JSDoc/TSDoc:**
|
||||
- JSDoc is uncommon. The clearest example is the security-sensitive note on `isValidApiKey` in `src/lib/auth.ts`.
|
||||
|
||||
## Function Design
|
||||
|
||||
**Size:**
|
||||
- Read/query modules tolerate large files with many helpers. `src/hooks/useProjects.ts` is 602 lines, `src/hooks/useHome.ts` is 250 lines, and `src/app/api/signals/route.ts` is 355 lines.
|
||||
- Keep complex logic split into local helpers inside the same file before extracting a new module. Current examples include `parseSlugList` in `src/app/api/projects/route.ts`, `parseSections` in `src/app/api/signals/route.ts`, and `collectValidationErrorsForProjectItem` in `src/app/api/tags/reset-projects/route.ts`.
|
||||
|
||||
**Parameters:**
|
||||
- Prefer a single typed options object for query functions, as in `getProjects` in `src/hooks/useProjects.ts`.
|
||||
- Use small helper functions for normalization of query and payload input, such as `normalizePositiveInteger` in `src/hooks/useProjects.ts` and `normalizeSlug` in `src/app/api/tags/reset-projects/route.ts`.
|
||||
|
||||
**Return Values:**
|
||||
- Return plain serializable objects from server query helpers when the result is meant for pages or APIs, as in `getProjects`, `getHomePageData`, and `getProjectsByIds`.
|
||||
- Flatten Prisma relation shapes before returning to callers when the UI expects direct lists, as in `getProjects`, `getProjectBySlug`, and `getProjectsByIds` in `src/hooks/useProjects.ts`.
|
||||
|
||||
## Environment and Configuration
|
||||
|
||||
**Environment Files:**
|
||||
- `.env`, `.env.local`, and `.env.example` are present at repository root. Use `.env.example` as the naming reference; do not commit real secrets.
|
||||
|
||||
**Observed Variables:**
|
||||
- `.env.example` defines `DATABASE_URL`, `WEBHOOK_API_KEY`, `N8N_AI_SEARCH_WEBHOOK`, `NEXT_INTL_DEFAULT_LOCALE`, and `NEXT_INTL_SUPPORTED_LOCALES`.
|
||||
- Source code reads `WEBHOOK_API_KEY` in `src/lib/auth.ts`, `N8N_AI_SEARCH_WEBHOOK` in `src/app/api/search/ai/route.ts`, `NEXT_PUBLIC_SITE_URL` in `src/app/robots.ts` and `src/app/sitemap.ts`, `VERCEL_ENV` in `src/app/layout.tsx`, and `NODE_ENV` in `src/lib/prisma.ts`.
|
||||
- `NEXT_INTL_DEFAULT_LOCALE` and `NEXT_INTL_SUPPORTED_LOCALES` appear in `.env.example` but were not detected in runtime code on 2026-04-18. Locale behavior is hard-coded in `src/middleware.ts` and `src/i18n/request.ts`.
|
||||
|
||||
**Conventions:**
|
||||
- Fail fast at module load only for truly required integration config. `src/app/api/search/ai/route.ts` throws immediately if `N8N_AI_SEARCH_WEBHOOK` is unset.
|
||||
- Use safe fallbacks for public metadata values, as in `src/app/robots.ts` and `src/app/sitemap.ts` defaulting `NEXT_PUBLIC_SITE_URL` to `https://agentpark.ai`.
|
||||
- Keep deployment and framework config in root files: `next.config.js`, `tailwind.config.ts`, `postcss.config.mjs`, `tsconfig.json`, `.eslintrc.json`, `.prettierrc.json`, and `vitest.config.ts`.
|
||||
|
||||
---
|
||||
|
||||
*Convention analysis: 2026-04-18*
|
||||
Reference in New Issue
Block a user