class-ai-agent 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/README.md +10 -5
- package/.agent/SESSION.md +18 -13
- package/.agent/rules/agent-continuity.md +44 -0
- package/.agent/rules/antigravity-overview.md +38 -0
- package/.agent/rules/api-conventions.md +85 -0
- package/.agent/rules/clean-code.md +211 -0
- package/.agent/rules/code-style.md +92 -0
- package/.agent/rules/codegraph.md +47 -0
- package/.agent/rules/database.md +66 -0
- package/.agent/rules/error-handling.md +98 -0
- package/.agent/rules/git-workflow.md +83 -0
- package/.agent/rules/monitoring.md +317 -0
- package/.agent/rules/naming-conventions.md +266 -0
- package/.agent/rules/project-structure.md +71 -0
- package/.agent/rules/security.md +95 -0
- package/.agent/rules/system-design.md +168 -0
- package/.agent/rules/tech-stack.md +463 -0
- package/.agent/rules/testing.md +110 -0
- package/.agents/agents/backend.md +395 -0
- package/.agents/agents/business-analyst.md +380 -0
- package/.agents/agents/code-reviewer.md +110 -0
- package/.agents/agents/copywriter-seo.md +236 -0
- package/.agents/agents/frontend.md +384 -0
- package/.agents/agents/project-manager.md +201 -0
- package/.agents/agents/qa.md +221 -0
- package/.agents/agents/security-auditor.md +143 -0
- package/.agents/agents/systems-architect.md +211 -0
- package/.agents/agents/test-engineer.md +123 -0
- package/.agents/agents/ui-ux-designer.md +210 -0
- package/.agents/references/accessibility-checklist.md +174 -0
- package/.agents/references/agent-continuity.md +42 -0
- package/.agents/references/codegraph.md +90 -0
- package/.agents/references/mcp-antigravity.md +71 -0
- package/.agents/references/performance-checklist.md +150 -0
- package/.agents/references/security-checklist.md +94 -0
- package/.agents/references/supabase.md +55 -0
- package/.agents/references/testing-patterns.md +183 -0
- package/.agents/skills/agent-continuity/SKILL.md +70 -0
- package/.agents/skills/code-review/SKILL.md +208 -0
- package/.agents/skills/deploy/SKILL.md +68 -0
- package/.agents/skills/deploy/deploy.md +735 -0
- package/.agents/skills/incremental-implementation/SKILL.md +210 -0
- package/.agents/skills/security-review/SKILL.md +71 -0
- package/.agents/skills/supabase/SKILL.md +135 -0
- package/.agents/skills/supabase/UPSTREAM.md +16 -0
- package/.agents/skills/supabase/assets/feedback-issue-template.md +17 -0
- package/.agents/skills/supabase/references/skill-feedback.md +17 -0
- package/.agents/skills/supabase-postgres-best-practices/SKILL.md +64 -0
- package/.agents/skills/supabase-postgres-best-practices/UPSTREAM.md +16 -0
- package/.agents/skills/supabase-postgres-best-practices/references/_contributing.md +170 -0
- package/.agents/skills/supabase-postgres-best-practices/references/_sections.md +39 -0
- package/.agents/skills/supabase-postgres-best-practices/references/_template.md +34 -0
- package/.agents/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-rls-performance.md +63 -0
- package/.agents/skills/tdd/SKILL.md +217 -0
- package/.agents/skills/ui-ux-pro-max/SKILL.md +288 -0
- package/.agents/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.agents/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/.agents/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/.agents/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/.agents/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/.agents/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.agents/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.agents/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/.agents/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/.agents/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.agents/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.agents/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.agents/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/.agents/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.agents/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/.agents/workflows/build.md +132 -0
- package/.agents/workflows/debug.md +242 -0
- package/.agents/workflows/deploy.md +43 -0
- package/.agents/workflows/fix-issue.md +45 -0
- package/.agents/workflows/handoff.md +93 -0
- package/.agents/workflows/plan.md +125 -0
- package/.agents/workflows/publish-npm.md +122 -0
- package/.agents/workflows/resume.md +106 -0
- package/.agents/workflows/review.md +53 -0
- package/.agents/workflows/simplify.md +221 -0
- package/.agents/workflows/spec.md +95 -0
- package/.agents/workflows/test.md +213 -0
- package/.claude/CLAUDE.md +23 -0
- package/.claude/agents/business-analyst.md +380 -0
- package/.claude/references/codegraph.md +26 -14
- package/.claude/rules/agent-continuity.md +3 -2
- package/.claude/rules/api-conventions.md +1 -0
- package/.claude/rules/clean-code.md +1 -0
- package/.claude/rules/code-style.md +1 -0
- package/.claude/rules/codegraph.md +43 -0
- package/.claude/rules/database.md +2 -1
- package/.claude/rules/error-handling.md +1 -0
- package/.claude/rules/git-workflow.md +1 -0
- package/.claude/rules/monitoring.md +1 -0
- package/.claude/rules/naming-conventions.md +1 -0
- package/.claude/rules/project-structure.md +1 -0
- package/.claude/rules/security.md +1 -0
- package/.claude/rules/system-design.md +1 -0
- package/.claude/rules/tech-stack.md +1 -0
- package/.claude/rules/testing.md +1 -0
- package/.claude/settings.json +3 -1
- package/.claude/skills/ui-ux-pro-max/SKILL.md +1 -90
- package/.cursor/CURSOR.md +1 -1
- package/.cursor/agents/business-analyst.md +380 -0
- package/.cursor/rules/cursor-overview.mdc +4 -3
- package/.cursor/rules/database.mdc +2 -2
- package/.kiro/KIRO.md +3 -3
- package/.kiro/agents/business-analyst.md +380 -0
- package/.kiro/steering/database.md +2 -2
- package/.kiro/steering/kiro-overview.md +2 -2
- package/AGENTS.md +23 -1
- package/GEMINI.md +152 -0
- package/README.md +65 -19
- package/bin/class-ai-agent.cjs +85 -9
- package/package.json +11 -4
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
---
|
|
2
|
+
trigger: glob
|
|
3
|
+
globs: {ts,tsx,js,jsx,mjs,cjs,json,md,prisma,yml,yaml}
|
|
4
|
+
description: "Technology Stack — Selection & Standards"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Technology Stack — Selection & Standards
|
|
8
|
+
|
|
9
|
+
> This rule defines the **approved tech stack** for all projects. When starting a new project or proposing a new dependency, follow the decision criteria below.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 🗂️ Quick Reference — Approved Stack
|
|
14
|
+
|
|
15
|
+
| Layer | Primary Choice | Alternative | Avoid |
|
|
16
|
+
|-------|---------------|-------------|-------|
|
|
17
|
+
| **Frontend — Landing/SEO** | Next.js 14+ (App Router) | — | CRA (deprecated) |
|
|
18
|
+
| **Frontend — Admin/Dashboard** | React + Vite (SPA) | — | Next.js (overkill for admin) |
|
|
19
|
+
| **UI Components** | shadcn/ui + Radix UI | Chakra UI | MUI (too heavy) |
|
|
20
|
+
| **Styling** | Tailwind CSS | CSS Modules | Styled-components (runtime cost) |
|
|
21
|
+
| **State Management** | Zustand | Redux Toolkit | MobX, Recoil |
|
|
22
|
+
| **Data Fetching** | TanStack Query (React Query) | SWR | Axios alone |
|
|
23
|
+
| **Backend Framework** | Express.js + Node | Fastify | Hapi, Koa |
|
|
24
|
+
| **API Style** | REST (default) | tRPC (fullstack TS) | GraphQL (unless needed) |
|
|
25
|
+
| **Language** | TypeScript (always) | — | Plain JavaScript |
|
|
26
|
+
| **Database** | PostgreSQL | Supabase (managed Postgres + bundled `supabase` skills) | MySQL (prefer PG) |
|
|
27
|
+
| **ORM** | Prisma | Drizzle | Sequelize, TypeORM |
|
|
28
|
+
| **BaaS (Auth, DB, Storage, Realtime)** | — | Supabase | Firebase |
|
|
29
|
+
| **Cache** | Redis (ioredis) | Upstash Redis | Memcached |
|
|
30
|
+
| **Queue — Simple jobs** | BullMQ (Redis-backed) | — | — |
|
|
31
|
+
| **Queue — Enterprise/Microservices** | RabbitMQ | Kafka (high-throughput streams) | — |
|
|
32
|
+
| **Auth** | NextAuth.js (Next) / JWT+bcrypt (API) | Lucia Auth | Firebase Auth |
|
|
33
|
+
| **File Storage** | AWS S3 / Cloudflare R2 | Supabase Storage | Local disk (not scalable) |
|
|
34
|
+
| **Email** | Resend | Nodemailer + SMTP | SendGrid (expensive) |
|
|
35
|
+
| **Search** | PostgreSQL FTS (start here) | Meilisearch | Elasticsearch (unless needed) |
|
|
36
|
+
| **Monitoring** | Grafana + Prometheus | Datadog | — |
|
|
37
|
+
| **Logging** | Pino | Winston | console.log (production) |
|
|
38
|
+
| **Testing** | Vitest + Testing Library | Jest | Mocha |
|
|
39
|
+
| **E2E Testing** | Playwright | Cypress | Selenium |
|
|
40
|
+
| **CI/CD** | GitHub Actions | — | Jenkins (legacy) |
|
|
41
|
+
| **Containerization** | Docker + Docker Compose | — | — |
|
|
42
|
+
| **Deployment** | Vercel (frontend) + Railway/Fly.io (backend) | AWS | — |
|
|
43
|
+
| **API Docs** | Swagger / OpenAPI 3.0 | — | Postman collections only |
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 🖥️ Frontend — Chọn đúng framework
|
|
48
|
+
|
|
49
|
+
### Decision Table
|
|
50
|
+
|
|
51
|
+
| Tiêu chí | Next.js 14 (App Router) | React + Vite (SPA) |
|
|
52
|
+
|----------|------------------------|--------------------|
|
|
53
|
+
| **Mục đích** | Landing page, marketing, blog | Admin panel, dashboard, internal tool |
|
|
54
|
+
| **SEO** | ✅ SSR/SSG — Google index tốt | ❌ SPA — khó SEO |
|
|
55
|
+
| **Lưu trữ** | Vercel (tối ưu nhất) | Cloudflare Pages, Netlify, S3 |
|
|
56
|
+
| **Performance** | Server Components — ít JS gửi về client | Client-side rendering |
|
|
57
|
+
| **Auth** | NextAuth.js | JWT stored in cookie/localStorage |
|
|
58
|
+
| **API** | API Routes hoặc Server Actions | Gọi backend REST riêng biệt |
|
|
59
|
+
| **Build complexity** | Cao hơn | Đơn giản hơn |
|
|
60
|
+
|
|
61
|
+
> **Rule**: Một project thường có **cả hai** — Next.js cho public site + React cho admin.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
### Next.js — Landing Page / SEO Project
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npx create-next-app@latest my-landing \
|
|
69
|
+
--typescript --tailwind --eslint --app --src-dir --import-alias "@/*"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Tại sao Next.js cho landing page:**
|
|
73
|
+
- Server-Side Rendering (SSR) → Google crawl được nội dung
|
|
74
|
+
- Static Site Generation (SSG) → build thành HTML tĩnh, lưu CDN, siêu nhanh
|
|
75
|
+
- Image optimization tự động (`next/image`)
|
|
76
|
+
- `<head>` metadata API tích hợp sẵn
|
|
77
|
+
- Incremental Static Regeneration (ISR) → cập nhật nội dung không rebuild toàn bộ
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
// app/layout.tsx — SEO metadata
|
|
81
|
+
export const metadata: Metadata = {
|
|
82
|
+
title: { default: 'My App', template: '%s | My App' },
|
|
83
|
+
description: 'Mô tả trang chính',
|
|
84
|
+
openGraph: { type: 'website', locale: 'vi_VN', url: 'https://myapp.com' },
|
|
85
|
+
};
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Folder structure (App Router)**
|
|
89
|
+
```
|
|
90
|
+
src/app/
|
|
91
|
+
├── (marketing)/ # Public pages (SSG/SSR)
|
|
92
|
+
│ ├── page.tsx # Homepage
|
|
93
|
+
│ ├── about/page.tsx
|
|
94
|
+
│ ├── blog/
|
|
95
|
+
│ │ ├── page.tsx # Blog list (SSG)
|
|
96
|
+
│ │ └── [slug]/page.tsx # Blog post (ISR)
|
|
97
|
+
│ └── pricing/page.tsx
|
|
98
|
+
├── (auth)/ # Auth pages
|
|
99
|
+
│ ├── login/page.tsx
|
|
100
|
+
│ └── register/page.tsx
|
|
101
|
+
├── api/v1/ # API Routes
|
|
102
|
+
└── layout.tsx
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
### React + Vite — Admin / Dashboard Project
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npx create-vite@latest my-admin -- --template react-ts
|
|
111
|
+
cd my-admin && npm install
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Tại sao React SPA cho admin:**
|
|
115
|
+
- Admin panel không cần SEO (đăng nhập mới vào được)
|
|
116
|
+
- SPA build đơn giản, deploy lên S3/Cloudflare Pages/Nginx
|
|
117
|
+
- Trạng thái phức tạp (table, filter, form) dễ quản lý hơn
|
|
118
|
+
- Hot reload nhanh hơn trong development
|
|
119
|
+
|
|
120
|
+
**Folder structure (Vite SPA)**
|
|
121
|
+
```
|
|
122
|
+
src/
|
|
123
|
+
├── pages/ # Các trang (react-router)
|
|
124
|
+
│ ├── Dashboard.tsx
|
|
125
|
+
│ ├── Users/
|
|
126
|
+
│ │ ├── UserList.tsx
|
|
127
|
+
│ │ └── UserDetail.tsx
|
|
128
|
+
│ └── Settings.tsx
|
|
129
|
+
├── components/
|
|
130
|
+
│ ├── layout/ # Sidebar, Header, Layout
|
|
131
|
+
│ └── ui/ # Shared UI components
|
|
132
|
+
├── features/ # Feature-based modules
|
|
133
|
+
│ └── users/
|
|
134
|
+
│ ├── api.ts # TanStack Query hooks
|
|
135
|
+
│ ├── store.ts # Zustand slice
|
|
136
|
+
│ └── types.ts
|
|
137
|
+
├── lib/ # axios instance, utils
|
|
138
|
+
└── main.tsx
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Key Rules cho Admin:**
|
|
142
|
+
- Protected routes với `<AuthGuard>` component
|
|
143
|
+
- Role-based UI: `usePermission()` hook ẩn/hiện features
|
|
144
|
+
- Token refresh tự động trong axios interceptor
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 🗄️ Database — PostgreSQL + Prisma
|
|
149
|
+
|
|
150
|
+
### Why PostgreSQL
|
|
151
|
+
- ACID compliant, battle-tested
|
|
152
|
+
- Excellent JSON support (`jsonb`) — avoids needing MongoDB in most cases
|
|
153
|
+
- Full-text search built-in
|
|
154
|
+
- Row-level security for multi-tenant apps
|
|
155
|
+
- Best ORM support (Prisma, Drizzle)
|
|
156
|
+
|
|
157
|
+
### Prisma Setup
|
|
158
|
+
```bash
|
|
159
|
+
npm install prisma @prisma/client
|
|
160
|
+
npx prisma init --datasource-provider postgresql
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Prisma Schema Conventions
|
|
164
|
+
```prisma
|
|
165
|
+
// prisma/schema.prisma
|
|
166
|
+
|
|
167
|
+
model User {
|
|
168
|
+
id String @id @default(cuid()) // ✅ cuid() for distributed systems
|
|
169
|
+
email String @unique
|
|
170
|
+
name String?
|
|
171
|
+
role Role @default(USER)
|
|
172
|
+
createdAt DateTime @default(now())
|
|
173
|
+
updatedAt DateTime @updatedAt
|
|
174
|
+
deletedAt DateTime? // soft delete
|
|
175
|
+
|
|
176
|
+
orders Order[]
|
|
177
|
+
|
|
178
|
+
@@map("users") // ✅ snake_case table name
|
|
179
|
+
@@index([email])
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
enum Role {
|
|
183
|
+
USER
|
|
184
|
+
ADMIN
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Prisma Client — Singleton Pattern
|
|
189
|
+
```ts
|
|
190
|
+
// src/lib/db.ts
|
|
191
|
+
import { PrismaClient } from '@prisma/client';
|
|
192
|
+
|
|
193
|
+
const globalForPrisma = global as unknown as { prisma: PrismaClient };
|
|
194
|
+
|
|
195
|
+
export const db =
|
|
196
|
+
globalForPrisma.prisma ||
|
|
197
|
+
new PrismaClient({
|
|
198
|
+
log: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'],
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db;
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Migration Workflow
|
|
205
|
+
```bash
|
|
206
|
+
# Development: auto-migrate
|
|
207
|
+
npx prisma migrate dev --name add_user_role
|
|
208
|
+
|
|
209
|
+
# Production: apply pending migrations
|
|
210
|
+
npx prisma migrate deploy
|
|
211
|
+
|
|
212
|
+
# View DB in browser
|
|
213
|
+
npx prisma studio
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### PostgreSQL Best Practices
|
|
217
|
+
- Use `cuid()` or `uuid()` for primary keys (not auto-increment integers for distributed systems)
|
|
218
|
+
- Always add `@@index` on foreign keys and frequently queried columns
|
|
219
|
+
- Use `jsonb` columns for flexible/schema-less data instead of adding MongoDB
|
|
220
|
+
- Enable `pg_trgm` extension for fuzzy search
|
|
221
|
+
- Set `statement_timeout` and `lock_timeout` for long queries
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## ⚡ Cache — Redis + ioredis
|
|
226
|
+
|
|
227
|
+
### Why Redis
|
|
228
|
+
- Sub-millisecond latency
|
|
229
|
+
- Supports strings, hashes, lists, sets, sorted sets, streams
|
|
230
|
+
- Built-in TTL, pub/sub, Lua scripts
|
|
231
|
+
- Powers caching + queues (BullMQ) + rate limiting + sessions
|
|
232
|
+
|
|
233
|
+
### Redis Client Setup
|
|
234
|
+
```ts
|
|
235
|
+
// src/lib/redis.ts
|
|
236
|
+
import Redis from 'ioredis';
|
|
237
|
+
|
|
238
|
+
const globalForRedis = global as unknown as { redis: Redis };
|
|
239
|
+
|
|
240
|
+
export const redis =
|
|
241
|
+
globalForRedis.redis ||
|
|
242
|
+
new Redis(process.env.REDIS_URL!, {
|
|
243
|
+
maxRetriesPerRequest: 3,
|
|
244
|
+
enableReadyCheck: true,
|
|
245
|
+
lazyConnect: true,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
if (process.env.NODE_ENV !== 'production') globalForRedis.redis = redis;
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Cache Helper
|
|
252
|
+
```ts
|
|
253
|
+
// src/lib/cache.ts
|
|
254
|
+
import { redis } from './redis';
|
|
255
|
+
|
|
256
|
+
export async function getOrSet<T>(
|
|
257
|
+
key: string,
|
|
258
|
+
fetcher: () => Promise<T>,
|
|
259
|
+
ttlSeconds = 3600
|
|
260
|
+
): Promise<T> {
|
|
261
|
+
const cached = await redis.get(key);
|
|
262
|
+
if (cached) return JSON.parse(cached);
|
|
263
|
+
|
|
264
|
+
const data = await fetcher();
|
|
265
|
+
await redis.setex(key, ttlSeconds, JSON.stringify(data));
|
|
266
|
+
return data;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export async function invalidate(pattern: string) {
|
|
270
|
+
const keys = await redis.keys(pattern);
|
|
271
|
+
if (keys.length) await redis.del(...keys);
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Redis Key Naming → See `naming-conventions.md`
|
|
276
|
+
```
|
|
277
|
+
myapp:v1:user:123:profile (TTL: 1h)
|
|
278
|
+
myapp:v1:session:abc123 (TTL: 7d)
|
|
279
|
+
myapp:v1:rate_limit:ip:... (TTL: 15m)
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Queue with BullMQ (Simple — default)
|
|
283
|
+
```ts
|
|
284
|
+
// src/queues/email-queue.ts
|
|
285
|
+
import { Queue, Worker } from 'bullmq';
|
|
286
|
+
import { redis } from '@/lib/redis';
|
|
287
|
+
|
|
288
|
+
export const emailQueue = new Queue('email', { connection: redis });
|
|
289
|
+
await emailQueue.add('send-welcome', { to: user.email, name: user.name });
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## 📨 Queue — Chọn đúng loại
|
|
295
|
+
|
|
296
|
+
### Decision Table
|
|
297
|
+
|
|
298
|
+
| Tiêu chí | BullMQ | RabbitMQ | Kafka |
|
|
299
|
+
|----------|--------|----------|-------|
|
|
300
|
+
| **Khi dùng** | Jobs đơn giản, retry, schedule | Microservices, routing phức tạp | Event streaming, log, billions messages |
|
|
301
|
+
| **Throughput** | Trung bình | Cao | Cực cao (triệu msg/s) |
|
|
302
|
+
| **Persistence** | Redis TTL | Disk (durable) | Disk (log-based, immutable) |
|
|
303
|
+
| **Setup** | Redis có sẵn | Cài thêm RabbitMQ | Cài thêm Kafka + Zookeeper |
|
|
304
|
+
| **Retry** | ✅ Built-in | ✅ Dead Letter Queue | ✅ Consumer offset |
|
|
305
|
+
| **Ordering** | ❌ Không đảm bảo | ✅ Per-queue | ✅ Per-partition |
|
|
306
|
+
| **Replay** | ❌ | ❌ | ✅ Có thể replay |
|
|
307
|
+
| **Độ phức tạp** | Thấp | Trung bình | Cao |
|
|
308
|
+
|
|
309
|
+
> **Rule**: Mặc định dùng **BullMQ**. Chỉ dùng RabbitMQ khi microservices. Chỉ dùng Kafka khi cần stream lớn hoặc replay.
|
|
310
|
+
|
|
311
|
+
### BullMQ — Default (Redis-backed)
|
|
312
|
+
```ts
|
|
313
|
+
// Khi nào: email, notification, PDF, image resize, scheduled tasks
|
|
314
|
+
const emailQueue = new Queue('email', {
|
|
315
|
+
connection: redis,
|
|
316
|
+
defaultJobOptions: { attempts: 3, backoff: { type: 'exponential', delay: 2000 } },
|
|
317
|
+
});
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### RabbitMQ — Microservices
|
|
321
|
+
```ts
|
|
322
|
+
// Khi nào: nhiều service cần nhận cùng 1 message (pub/sub), routing phức tạp
|
|
323
|
+
import amqplib from 'amqplib';
|
|
324
|
+
|
|
325
|
+
const conn = await amqplib.connect(process.env.RABBITMQ_URL!);
|
|
326
|
+
const channel = await conn.createChannel();
|
|
327
|
+
|
|
328
|
+
// Exchange-based routing
|
|
329
|
+
await channel.assertExchange('order.events', 'topic', { durable: true });
|
|
330
|
+
await channel.publish('order.events', 'order.placed', Buffer.from(JSON.stringify(payload)));
|
|
331
|
+
|
|
332
|
+
// Consumer
|
|
333
|
+
await channel.assertQueue('email-service.order.placed', { durable: true });
|
|
334
|
+
await channel.bindQueue('email-service.order.placed', 'order.events', 'order.placed');
|
|
335
|
+
channel.consume('email-service.order.placed', async (msg) => {
|
|
336
|
+
if (!msg) return;
|
|
337
|
+
const data = JSON.parse(msg.content.toString());
|
|
338
|
+
await sendOrderEmail(data);
|
|
339
|
+
channel.ack(msg);
|
|
340
|
+
});
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Kafka — High-throughput Streaming
|
|
344
|
+
```ts
|
|
345
|
+
// Khi nào: analytics events, audit logs, real-time feeds, > 100k msg/s
|
|
346
|
+
import { Kafka } from 'kafkajs';
|
|
347
|
+
|
|
348
|
+
const kafka = new Kafka({ brokers: [process.env.KAFKA_BROKER!] });
|
|
349
|
+
|
|
350
|
+
// Producer
|
|
351
|
+
const producer = kafka.producer();
|
|
352
|
+
await producer.send({
|
|
353
|
+
topic: 'user-events',
|
|
354
|
+
messages: [{ key: userId, value: JSON.stringify({ event: 'page_viewed', page: '/checkout' }) }],
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// Consumer group
|
|
358
|
+
const consumer = kafka.consumer({ groupId: 'analytics-service' });
|
|
359
|
+
await consumer.subscribe({ topic: 'user-events', fromBeginning: false });
|
|
360
|
+
await consumer.run({
|
|
361
|
+
eachMessage: async ({ message }) => {
|
|
362
|
+
await analyticsService.track(JSON.parse(message.value!.toString()));
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Queue Naming → See `naming-conventions.md`
|
|
368
|
+
```
|
|
369
|
+
BullMQ queue names: myapp.email.send
|
|
370
|
+
RabbitMQ exchange: order.events (type: topic)
|
|
371
|
+
RabbitMQ queue: email-service.order.placed
|
|
372
|
+
Kafka topic: user-events, order-events, payment-events
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
## 📄 Documentation
|
|
378
|
+
|
|
379
|
+
### API Documentation — OpenAPI / Swagger
|
|
380
|
+
```bash
|
|
381
|
+
npm install swagger-ui-express @asteasolutions/zod-to-openapi
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
- Every API endpoint MUST have OpenAPI annotations
|
|
385
|
+
- Auto-generate from code (Zod schemas or JSDoc)
|
|
386
|
+
- Mount at `/api-docs`
|
|
387
|
+
- Keep `openapi.yaml` committed to repo
|
|
388
|
+
|
|
389
|
+
### Code Documentation
|
|
390
|
+
```ts
|
|
391
|
+
/**
|
|
392
|
+
* Find a user by their email address.
|
|
393
|
+
* @param email - The user's email (must be lowercase)
|
|
394
|
+
* @returns The user object or null if not found
|
|
395
|
+
* @throws {AppError} If database is unavailable
|
|
396
|
+
*/
|
|
397
|
+
async function findUserByEmail(email: string): Promise<User | null> {}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### README Template (mandatory for every service)
|
|
401
|
+
```markdown
|
|
402
|
+
# Service Name
|
|
403
|
+
|
|
404
|
+
## What it does (1-2 sentences)
|
|
405
|
+
|
|
406
|
+
## Tech Stack
|
|
407
|
+
- Runtime: Node.js 20 + TypeScript
|
|
408
|
+
- Framework: Next.js 14
|
|
409
|
+
- Database: PostgreSQL (Prisma)
|
|
410
|
+
- Cache: Redis
|
|
411
|
+
|
|
412
|
+
## Quick Start
|
|
413
|
+
\`\`\`bash
|
|
414
|
+
cp .env.example .env
|
|
415
|
+
npm install
|
|
416
|
+
npx prisma migrate dev
|
|
417
|
+
npm run dev
|
|
418
|
+
\`\`\`
|
|
419
|
+
|
|
420
|
+
## Environment Variables → see .env.example
|
|
421
|
+
## API Documentation → /api-docs
|
|
422
|
+
## Architecture → docs/architecture.md
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Architecture Diagrams (docs/architecture/)
|
|
426
|
+
- Use **Mermaid** for all diagrams (version-controlled, no external tools)
|
|
427
|
+
- Required diagrams: System context, Component diagram, Data flow, DB ERD
|
|
428
|
+
|
|
429
|
+
```mermaid
|
|
430
|
+
graph LR
|
|
431
|
+
Client --> NextJS
|
|
432
|
+
NextJS --> PostgreSQL
|
|
433
|
+
NextJS --> Redis
|
|
434
|
+
NextJS --> Queue[BullMQ Queue]
|
|
435
|
+
Queue --> Worker[Background Worker]
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
## ✅ Technology Decision Process
|
|
441
|
+
|
|
442
|
+
When **proposing a new library or technology**, evaluate against these criteria:
|
|
443
|
+
|
|
444
|
+
| Criterion | Questions to ask |
|
|
445
|
+
|-----------|-----------------|
|
|
446
|
+
| **Necessity** | Does an approved alternative already solve this? |
|
|
447
|
+
| **Maintenance** | Stars > 1k? Last commit < 6 months? |
|
|
448
|
+
| **Bundle size** | Check bundlephobia.com — is it worth the KB? |
|
|
449
|
+
| **TypeScript** | Does it have native TS types? |
|
|
450
|
+
| **License** | Is it MIT/Apache? (No GPL in commercial products) |
|
|
451
|
+
| **Security** | `npm audit` — zero high/critical vulnerabilities |
|
|
452
|
+
| **Community** | Active issues/discussions? Stack Overflow answers? |
|
|
453
|
+
|
|
454
|
+
### Decision Template
|
|
455
|
+
```markdown
|
|
456
|
+
## Technology Decision: [Library Name]
|
|
457
|
+
|
|
458
|
+
**Problem**: What problem does this solve?
|
|
459
|
+
**Alternative evaluated**: What from the approved stack was considered?
|
|
460
|
+
**Why chosen**: Specific reason this is better for the use case
|
|
461
|
+
**Risk**: Known downsides or migration cost
|
|
462
|
+
**Decision**: ✅ Adopt / ❌ Reject
|
|
463
|
+
```
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
trigger: glob
|
|
3
|
+
globs: {ts,tsx,js,jsx,mjs,cjs,json,md,prisma,yml,yaml}
|
|
4
|
+
description: "Testing Standards"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Testing Standards
|
|
8
|
+
|
|
9
|
+
## Testing Pyramid
|
|
10
|
+
```
|
|
11
|
+
[E2E Tests] ← Few, slow, catch integration issues
|
|
12
|
+
[Integration Tests] ← Some, test component interaction
|
|
13
|
+
[Unit Tests] ← Many, fast, test isolated logic
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Requirements
|
|
17
|
+
- Unit test coverage: **minimum 80%**
|
|
18
|
+
- All new features must have tests
|
|
19
|
+
- All bug fixes must have a regression test
|
|
20
|
+
- Tests run in CI before any merge
|
|
21
|
+
|
|
22
|
+
## Test File Organization
|
|
23
|
+
```
|
|
24
|
+
tests/
|
|
25
|
+
├── unit/
|
|
26
|
+
│ ├── services/user-service.test.js
|
|
27
|
+
│ └── utils/format.test.js
|
|
28
|
+
├── integration/
|
|
29
|
+
│ ├── routes/users.test.js
|
|
30
|
+
│ └── database/user-repo.test.js
|
|
31
|
+
└── e2e/
|
|
32
|
+
└── auth-flow.test.js
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Unit Test Example (Jest)
|
|
36
|
+
```js
|
|
37
|
+
// tests/unit/services/user-service.test.js
|
|
38
|
+
import { UserService } from '../../../src/services/user-service.js';
|
|
39
|
+
import { mockUserRepository } from '../../mocks/user-repository.mock.js';
|
|
40
|
+
|
|
41
|
+
describe('UserService', () => {
|
|
42
|
+
let userService;
|
|
43
|
+
let mockRepo;
|
|
44
|
+
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
mockRepo = mockUserRepository();
|
|
47
|
+
userService = new UserService(mockRepo);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('findById', () => {
|
|
51
|
+
it('should return user when found', async () => {
|
|
52
|
+
const mockUser = { id: '1', email: 'test@test.com' };
|
|
53
|
+
mockRepo.findById.mockResolvedValue(mockUser);
|
|
54
|
+
|
|
55
|
+
const result = await userService.findById('1');
|
|
56
|
+
|
|
57
|
+
expect(result).toEqual(mockUser);
|
|
58
|
+
expect(mockRepo.findById).toHaveBeenCalledWith('1');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should throw AppError when user not found', async () => {
|
|
62
|
+
mockRepo.findById.mockResolvedValue(null);
|
|
63
|
+
|
|
64
|
+
await expect(userService.findById('999'))
|
|
65
|
+
.rejects.toThrow('User not found');
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Integration Test Example
|
|
72
|
+
```js
|
|
73
|
+
// tests/integration/routes/users.test.js
|
|
74
|
+
import request from 'supertest';
|
|
75
|
+
import app from '../../../src/index.js';
|
|
76
|
+
|
|
77
|
+
describe('GET /api/users/:id', () => {
|
|
78
|
+
it('should return 200 with user data', async () => {
|
|
79
|
+
const res = await request(app)
|
|
80
|
+
.get('/api/users/1')
|
|
81
|
+
.set('Authorization', `Bearer ${testToken}`);
|
|
82
|
+
|
|
83
|
+
expect(res.status).toBe(200);
|
|
84
|
+
expect(res.body.success).toBe(true);
|
|
85
|
+
expect(res.body.data).toHaveProperty('id');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should return 404 for unknown user', async () => {
|
|
89
|
+
const res = await request(app)
|
|
90
|
+
.get('/api/users/99999')
|
|
91
|
+
.set('Authorization', `Bearer ${testToken}`);
|
|
92
|
+
|
|
93
|
+
expect(res.status).toBe(404);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Test Commands
|
|
99
|
+
```bash
|
|
100
|
+
npm test # Run all tests
|
|
101
|
+
npm run test:unit # Unit tests only
|
|
102
|
+
npm run test:integration # Integration tests only
|
|
103
|
+
npm run test:coverage # Generate coverage report
|
|
104
|
+
npm run test:watch # Watch mode for development
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Naming Conventions
|
|
108
|
+
- Test files: `[filename].test.js`
|
|
109
|
+
- `describe` blocks: Match the module/function being tested
|
|
110
|
+
- `it` blocks: `should [expected behavior] when [condition]`
|