docs: enhance AGENTS.md files with Go linting, DI patterns, barrel re-export, TS best practices [guardrail-change]
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -27,15 +27,20 @@ components/ # Truly shared, app-wide components.
|
||||
## API routes (route.ts)
|
||||
|
||||
- One handler per HTTP method, ≤40 LOC.
|
||||
- Validate input with `zod`. Reject invalid → 400.
|
||||
- Validate input with zod `safeParse` — never `parse` (throws and bypasses error handling).
|
||||
- Delegate to `lib/server/<domain>/`. No business logic in `route.ts`.
|
||||
- Always return `NextResponse.json(..., { status })`. Never throw to the framework.
|
||||
- Always return `NextResponse.json(..., { status })`. Let the framework's error boundary handle unexpected errors — don't wrap the entire handler in `try/catch`.
|
||||
|
||||
```ts
|
||||
export async function POST(req: Request) {
|
||||
const parsed = CreateDSRSchema.safeParse(await req.json());
|
||||
if (!parsed.success) return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
|
||||
const result = await dsrService.create(parsed.data);
|
||||
```typescript
|
||||
// app/api/<domain>/route.ts (≤40 LOC)
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { mySchema } from '@/lib/schemas/<domain>';
|
||||
import { myService } from '@/lib/server/<domain>';
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const body = mySchema.safeParse(await req.json());
|
||||
if (!body.success) return NextResponse.json({ error: body.error }, { status: 400 });
|
||||
const result = await myService.create(body.data);
|
||||
return NextResponse.json(result, { status: 201 });
|
||||
}
|
||||
```
|
||||
@@ -52,6 +57,39 @@ export async function POST(req: Request) {
|
||||
- `lib/sdk/types.ts` is being split into `lib/sdk/types/<domain>.ts`. Mirror backend domain boundaries.
|
||||
- All API DTOs are zod schemas; infer types via `z.infer`.
|
||||
- No `any`. No `as unknown as`. If you reach for it, the type is wrong.
|
||||
- Always use `import type { Foo }` for type-only imports.
|
||||
- Never use `as` type assertions except when bridging external data at a boundary (add a comment explaining why).
|
||||
- No `@ts-ignore`. `@ts-expect-error` only with a comment explaining the suppression.
|
||||
|
||||
## Barrel re-export pattern
|
||||
|
||||
`lib/sdk/types.ts` is a barrel — it re-exports from domain-specific files. **Do not add new types directly to it.**
|
||||
|
||||
```typescript
|
||||
// lib/sdk/types.ts (barrel — DO NOT ADD NEW TYPES HERE)
|
||||
export * from './types/enums';
|
||||
export * from './types/company-profile';
|
||||
// ... etc.
|
||||
|
||||
// New types go in lib/sdk/types/<domain>.ts
|
||||
```
|
||||
|
||||
- When splitting an oversized file, keep the original as a thin barrel so existing imports don't break.
|
||||
- New code imports directly from the specific module (e.g. `import type { CompanyProfile } from '@/lib/sdk/types/company-profile'`), not the barrel.
|
||||
|
||||
## Server vs Client components
|
||||
|
||||
Default is Server Component. Add `"use client"` only when required:
|
||||
|
||||
| Need | Pattern |
|
||||
|------|---------|
|
||||
| Data fetching only | Server Component (no directive) |
|
||||
| `useState` / `useEffect` | Client Component (`"use client"`) |
|
||||
| Browser API | Client Component |
|
||||
| Event handlers | Client Component |
|
||||
|
||||
- Pass only serializable props from Server → Client Components (no functions, no class instances).
|
||||
- Never add `"use client"` to a layout or page just because one child needs it — extract the client part into a `_components/` file.
|
||||
|
||||
## Tests
|
||||
|
||||
@@ -78,8 +116,10 @@ export async function POST(req: Request) {
|
||||
|
||||
- Put business logic in a `page.tsx` or `route.ts`.
|
||||
- Reach across module boundaries (e.g. `admin-compliance` importing from `developer-portal`).
|
||||
- Use `dangerouslySetInnerHTML` without explicit sanitization.
|
||||
- Call backend APIs directly from Client Components when a Server Component or Server Action would do.
|
||||
- Use `dangerouslySetInnerHTML` without DOMPurify sanitization.
|
||||
- Call internal backend APIs directly from Client Components — use Server Components or API routes as a proxy.
|
||||
- Add `"use client"` to a layout or page just because one child needs it — extract the client part.
|
||||
- Spread `...props` onto a DOM element without filtering the props first (type error risk).
|
||||
- Change a public API route's path/method/schema without updating SDK consumers in the same change.
|
||||
- Create a file >500 lines.
|
||||
- Disable a lint or type rule globally to silence a finding — fix the root cause.
|
||||
|
||||
Reference in New Issue
Block a user