# 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*