create-tigra 1.1.0 → 2.0.1
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/LICENSE +21 -21
- package/README.md +80 -87
- package/bin/create-tigra.js +259 -308
- package/package.json +49 -41
- package/template/_claude/QUICK_REFERENCE.md +193 -0
- package/template/_claude/README.md +53 -0
- package/template/_claude/commands/create-client.md +881 -0
- package/template/_claude/commands/create-server.md +383 -0
- package/template/_claude/rules/client/01-project-structure.md +133 -0
- package/template/_claude/rules/client/02-components-and-types.md +146 -0
- package/template/_claude/rules/client/03-data-and-state.md +156 -0
- package/template/_claude/rules/client/04-design-system.md +185 -0
- package/template/_claude/rules/client/05-security.md +55 -0
- package/template/_claude/rules/client/06-ux-checklist.md +81 -0
- package/template/_claude/rules/client/core.md +42 -0
- package/template/_claude/rules/global/core.md +77 -0
- package/template/_claude/rules/server/core.md +50 -0
- package/template/_claude/rules/server/database.md +124 -0
- package/template/_claude/rules/server/project-conventions.md +150 -0
- package/template/_claude/rules/server/response-handling.md +144 -0
- package/template/client/.env.example +5 -0
- package/template/client/README.md +36 -0
- package/template/client/components.json +23 -0
- package/template/client/eslint.config.mjs +18 -0
- package/template/client/next.config.ts +34 -0
- package/template/client/package.json +44 -0
- package/template/client/postcss.config.mjs +7 -0
- package/template/client/src/app/(auth)/layout.tsx +18 -0
- package/template/client/src/app/(auth)/login/page.tsx +13 -0
- package/template/client/src/app/(auth)/register/page.tsx +13 -0
- package/template/client/src/app/(main)/dashboard/page.tsx +22 -0
- package/template/client/src/app/(main)/layout.tsx +11 -0
- package/template/client/src/app/error.tsx +27 -0
- package/template/client/src/app/favicon.ico +0 -0
- package/template/client/src/app/globals.css +145 -0
- package/template/client/src/app/layout.tsx +36 -0
- package/template/client/src/app/loading.tsx +11 -0
- package/template/client/src/app/not-found.tsx +23 -0
- package/template/client/src/app/page.tsx +45 -0
- package/template/client/src/app/providers.tsx +43 -0
- package/template/client/src/components/common/ConfirmDialog.tsx +56 -0
- package/template/client/src/components/common/EmptyState.tsx +31 -0
- package/template/client/src/components/common/LoadingSpinner.tsx +30 -0
- package/template/client/src/components/common/Pagination.tsx +55 -0
- package/template/client/src/components/layout/Footer.tsx +17 -0
- package/template/client/src/components/layout/Header.tsx +173 -0
- package/template/client/src/components/layout/MainLayout.tsx +18 -0
- package/template/client/src/components/ui/alert-dialog.tsx +196 -0
- package/template/client/src/components/ui/badge.tsx +48 -0
- package/template/client/src/components/ui/button.tsx +64 -0
- package/template/client/src/components/ui/card.tsx +92 -0
- package/template/client/src/components/ui/input.tsx +21 -0
- package/template/client/src/components/ui/label.tsx +24 -0
- package/template/client/src/components/ui/select.tsx +190 -0
- package/template/client/src/components/ui/skeleton.tsx +13 -0
- package/template/client/src/components/ui/table.tsx +116 -0
- package/template/client/src/features/auth/components/AuthInitializer.tsx +55 -0
- package/template/client/src/features/auth/components/LoginForm.tsx +107 -0
- package/template/client/src/features/auth/components/RegisterForm.tsx +178 -0
- package/template/client/src/features/auth/hooks/useAuth.ts +84 -0
- package/template/client/src/features/auth/services/auth.service.ts +52 -0
- package/template/client/src/features/auth/store/authSlice.ts +38 -0
- package/template/client/src/features/auth/types/auth.types.ts +32 -0
- package/template/client/src/hooks/useDebounce.ts +14 -0
- package/template/client/src/hooks/useLocalStorage.ts +55 -0
- package/template/client/src/hooks/useMediaQuery.ts +27 -0
- package/template/client/src/lib/api/api.types.ts +34 -0
- package/template/client/src/lib/api/axios.config.ts +98 -0
- package/template/client/src/lib/constants/api-endpoints.ts +18 -0
- package/template/client/src/lib/constants/app.constants.ts +12 -0
- package/template/client/src/lib/constants/routes.ts +9 -0
- package/template/client/src/lib/utils/error.ts +32 -0
- package/template/client/src/lib/utils/format.ts +37 -0
- package/template/client/src/lib/utils/security.ts +34 -0
- package/template/client/src/lib/utils.ts +6 -0
- package/template/client/src/middleware.ts +57 -0
- package/template/client/src/store/hooks.ts +7 -0
- package/template/client/src/store/index.ts +12 -0
- package/template/client/src/types/index.ts +3 -0
- package/template/client/tsconfig.json +34 -0
- package/template/gitignore +34 -0
- package/template/server/.dockerignore +66 -0
- package/template/server/.env.example +96 -69
- package/template/server/.env.production.example +90 -0
- package/template/server/Dockerfile +94 -0
- package/template/server/docker-compose.yml +82 -111
- package/template/server/docs/logging.md +62 -0
- package/template/server/eslint.config.mjs +17 -0
- package/template/server/package.json +68 -81
- package/template/server/phpmyadmin-config.php +26 -0
- package/template/server/postman_collection.json +666 -0
- package/template/server/prisma/schema.prisma +77 -93
- package/template/server/prisma/seed.ts +46 -142
- package/template/server/scripts/flush-redis.ts +41 -0
- package/template/server/src/app.ts +243 -71
- package/template/server/src/config/env.ts +67 -94
- package/template/server/src/libs/auth.ts +88 -0
- package/template/server/src/libs/cleanup.ts +35 -0
- package/template/server/src/libs/cookies.ts +46 -0
- package/template/server/src/libs/logger.ts +33 -60
- package/template/server/src/libs/monitoring.ts +205 -0
- package/template/server/src/libs/password.ts +38 -0
- package/template/server/src/libs/prisma.ts +68 -0
- package/template/server/src/libs/redis.ts +60 -79
- package/template/server/src/libs/requestLogger.ts +66 -0
- package/template/server/src/libs/storage/file-storage.service.ts +211 -0
- package/template/server/src/libs/storage/file-validator.ts +97 -0
- package/template/server/src/libs/storage/filename-sanitizer.ts +71 -0
- package/template/server/src/libs/storage/image-optimizer.service.ts +144 -0
- package/template/server/src/modules/auth/__tests__/auth.service.test.ts +365 -0
- package/template/server/src/modules/auth/auth.controller.ts +90 -141
- package/template/server/src/modules/auth/auth.repo.ts +120 -218
- package/template/server/src/modules/auth/auth.routes.ts +96 -83
- package/template/server/src/modules/auth/auth.schemas.ts +35 -137
- package/template/server/src/modules/auth/auth.service.ts +286 -329
- package/template/server/src/modules/auth/session.repo.ts +110 -0
- package/template/server/src/modules/users/users.controller.ts +120 -0
- package/template/server/src/modules/users/users.repo.ts +77 -0
- package/template/server/src/modules/users/users.routes.ts +89 -0
- package/template/server/src/modules/users/users.schemas.ts +21 -0
- package/template/server/src/modules/users/users.service.ts +169 -0
- package/template/server/src/server.ts +58 -139
- package/template/server/src/shared/errors/AppError.ts +21 -0
- package/template/server/src/shared/errors/errors.ts +43 -0
- package/template/server/src/shared/responses/paginatedResponse.ts +38 -0
- package/template/server/src/shared/responses/successResponse.ts +17 -0
- package/template/server/src/shared/schemas/pagination.schema.ts +12 -0
- package/template/server/src/shared/types/index.ts +26 -0
- package/template/server/src/test/setup.ts +74 -38
- package/template/server/tsconfig.json +27 -89
- package/template/server/uploads/avatars/.gitkeep +1 -0
- package/template/server/vitest.config.ts +43 -98
- package/template/.agent/rules/client/01-project-structure.md +0 -326
- package/template/.agent/rules/client/02-component-patterns.md +0 -249
- package/template/.agent/rules/client/03-typescript-rules.md +0 -226
- package/template/.agent/rules/client/04-state-management.md +0 -474
- package/template/.agent/rules/client/05-api-integration.md +0 -129
- package/template/.agent/rules/client/06-forms-validation.md +0 -129
- package/template/.agent/rules/client/07-common-patterns.md +0 -150
- package/template/.agent/rules/client/08-color-system.md +0 -93
- package/template/.agent/rules/client/09-security-rules.md +0 -97
- package/template/.agent/rules/client/10-testing-strategy.md +0 -370
- package/template/.agent/rules/global/ai-edit-safety.md +0 -38
- package/template/.agent/rules/server/01-db-and-migrations.md +0 -242
- package/template/.agent/rules/server/02-general-rules.md +0 -111
- package/template/.agent/rules/server/03-migrations.md +0 -20
- package/template/.agent/rules/server/04-pagination.md +0 -130
- package/template/.agent/rules/server/05-project-conventions.md +0 -71
- package/template/.agent/rules/server/06-response-handling.md +0 -173
- package/template/.agent/rules/server/07-testing-strategy.md +0 -506
- package/template/.agent/rules/server/08-observability.md +0 -180
- package/template/.agent/rules/server/10-background-jobs-v2.md +0 -185
- package/template/.agent/rules/server/11-rate-limiting-v2.md +0 -210
- package/template/.agent/rules/server/12-performance-optimization.md +0 -567
- package/template/.claude/rules/client-01-project-structure.md +0 -327
- package/template/.claude/rules/client-02-component-patterns.md +0 -250
- package/template/.claude/rules/client-03-typescript-rules.md +0 -227
- package/template/.claude/rules/client-04-state-management.md +0 -475
- package/template/.claude/rules/client-05-api-integration.md +0 -130
- package/template/.claude/rules/client-06-forms-validation.md +0 -130
- package/template/.claude/rules/client-07-common-patterns.md +0 -151
- package/template/.claude/rules/client-08-color-system.md +0 -94
- package/template/.claude/rules/client-09-security-rules.md +0 -98
- package/template/.claude/rules/client-10-testing-strategy.md +0 -371
- package/template/.claude/rules/global-ai-edit-safety.md +0 -39
- package/template/.claude/rules/server-01-db-and-migrations.md +0 -243
- package/template/.claude/rules/server-02-general-rules.md +0 -112
- package/template/.claude/rules/server-03-migrations.md +0 -21
- package/template/.claude/rules/server-04-pagination.md +0 -131
- package/template/.claude/rules/server-05-project-conventions.md +0 -72
- package/template/.claude/rules/server-06-response-handling.md +0 -174
- package/template/.claude/rules/server-07-testing-strategy.md +0 -507
- package/template/.claude/rules/server-08-observability.md +0 -181
- package/template/.claude/rules/server-10-background-jobs-v2.md +0 -186
- package/template/.claude/rules/server-11-rate-limiting-v2.md +0 -211
- package/template/.claude/rules/server-12-performance-optimization.md +0 -568
- package/template/.cursor/rules/client-01-project-structure.mdc +0 -327
- package/template/.cursor/rules/client-02-component-patterns.mdc +0 -250
- package/template/.cursor/rules/client-03-typescript-rules.mdc +0 -227
- package/template/.cursor/rules/client-04-state-management.mdc +0 -475
- package/template/.cursor/rules/client-05-api-integration.mdc +0 -130
- package/template/.cursor/rules/client-06-forms-validation.mdc +0 -130
- package/template/.cursor/rules/client-07-common-patterns.mdc +0 -151
- package/template/.cursor/rules/client-08-color-system.mdc +0 -94
- package/template/.cursor/rules/client-09-security-rules.mdc +0 -98
- package/template/.cursor/rules/client-10-testing-strategy.mdc +0 -371
- package/template/.cursor/rules/global-ai-edit-safety.mdc +0 -39
- package/template/.cursor/rules/server-01-db-and-migrations.mdc +0 -243
- package/template/.cursor/rules/server-02-general-rules.mdc +0 -112
- package/template/.cursor/rules/server-03-migrations.mdc +0 -21
- package/template/.cursor/rules/server-04-pagination.mdc +0 -131
- package/template/.cursor/rules/server-05-project-conventions.mdc +0 -72
- package/template/.cursor/rules/server-06-response-handling.mdc +0 -174
- package/template/.cursor/rules/server-07-testing-strategy.mdc +0 -507
- package/template/.cursor/rules/server-08-observability.mdc +0 -181
- package/template/.cursor/rules/server-09-api-documentation-v2.mdc +0 -169
- package/template/.cursor/rules/server-10-background-jobs-v2.mdc +0 -186
- package/template/.cursor/rules/server-11-rate-limiting-v2.mdc +0 -211
- package/template/.cursor/rules/server-12-performance-optimization.mdc +0 -568
- package/template/CLAUDE.md +0 -207
- package/template/server/.tsc-aliasrc.json +0 -13
- package/template/server/IMPORT_FIX_CHECKLIST.md +0 -98
- package/template/server/IMPORT_FIX_COMPLETE.md +0 -89
- package/template/server/README.md +0 -183
- package/template/server/REMAINING_IMPORT_FIXES.md +0 -150
- package/template/server/SECURITY.md +0 -190
- package/template/server/Tigra-API.postman_collection.json +0 -733
- package/template/server/biome.json +0 -42
- package/template/server/scripts/fix-all-imports.ps1 +0 -52
- package/template/server/scripts/fix-imports-reference.ps1 +0 -16
- package/template/server/scripts/fix-imports.mjs +0 -55
- package/template/server/scripts/setup-env.js +0 -50
- package/template/server/scripts/wait-for-db.js +0 -60
- package/template/server/src/hooks/request-timing.hook.ts +0 -26
- package/template/server/src/libs/auth/authenticate.middleware.ts +0 -22
- package/template/server/src/libs/auth/rbac.middleware.test.ts +0 -134
- package/template/server/src/libs/auth/rbac.middleware.ts +0 -147
- package/template/server/src/libs/db.ts +0 -76
- package/template/server/src/libs/error-handler.ts +0 -89
- package/template/server/src/libs/queue.ts +0 -79
- package/template/server/src/modules/admin/admin.controller.ts +0 -122
- package/template/server/src/modules/admin/admin.routes.ts +0 -62
- package/template/server/src/modules/admin/admin.schemas.ts +0 -35
- package/template/server/src/modules/admin/admin.service.ts +0 -167
- package/template/server/src/modules/auth/auth.integration.test.ts +0 -150
- package/template/server/src/modules/auth/auth.service.test.ts +0 -119
- package/template/server/src/modules/auth/auth.types.ts +0 -97
- package/template/server/src/modules/resources/resources.controller.ts +0 -218
- package/template/server/src/modules/resources/resources.repo.ts +0 -253
- package/template/server/src/modules/resources/resources.routes.ts +0 -116
- package/template/server/src/modules/resources/resources.schemas.ts +0 -146
- package/template/server/src/modules/resources/resources.service.ts +0 -218
- package/template/server/src/modules/resources/resources.types.ts +0 -73
- package/template/server/src/plugins/rate-limit.plugin.ts +0 -21
- package/template/server/src/plugins/security.plugin.ts +0 -21
- package/template/server/src/routes/health.routes.ts +0 -31
- package/template/server/src/types/fastify.d.ts +0 -36
- package/template/server/src/utils/errors.ts +0 -108
- package/template/server/src/utils/pagination.ts +0 -120
- package/template/server/src/utils/response.ts +0 -110
- package/template/server/src/workers/file.worker.ts +0 -106
- package/template/server/tsconfig.build.json +0 -30
- package/template/server/tsconfig.test.json +0 -22
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
# Create Server Project
|
|
2
|
+
|
|
3
|
+
You are scaffolding a new Fastify + TypeScript backend project. The project name is: **$ARGUMENTS**
|
|
4
|
+
|
|
5
|
+
If no project name is provided, ask the user for one before proceeding.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Instructions
|
|
10
|
+
|
|
11
|
+
Create a complete, production-ready Fastify server project following the architecture and rules defined in `.claude/rules/server/`. Read those rules before generating any code.
|
|
12
|
+
|
|
13
|
+
### Step 1: Project Root Setup
|
|
14
|
+
|
|
15
|
+
Create the project directory named `$ARGUMENTS` with the following root files:
|
|
16
|
+
|
|
17
|
+
#### `package.json`
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"name": "$ARGUMENTS",
|
|
21
|
+
"version": "1.0.0",
|
|
22
|
+
"description": "",
|
|
23
|
+
"main": "dist/server.js",
|
|
24
|
+
"scripts": {
|
|
25
|
+
"dev": "tsx watch src/server.ts",
|
|
26
|
+
"build": "tsc && tsc-alias",
|
|
27
|
+
"start": "node dist/server.js",
|
|
28
|
+
"prisma:generate": "prisma generate",
|
|
29
|
+
"prisma:migrate:dev": "prisma migrate dev",
|
|
30
|
+
"prisma:migrate:deploy": "prisma migrate deploy",
|
|
31
|
+
"prisma:reset": "prisma migrate reset",
|
|
32
|
+
"prisma:seed": "prisma db seed",
|
|
33
|
+
"prisma:studio": "prisma studio",
|
|
34
|
+
"lint": "eslint src/",
|
|
35
|
+
"docker:up": "docker compose up -d",
|
|
36
|
+
"docker:down": "docker compose down",
|
|
37
|
+
"docker:logs": "docker compose logs -f"
|
|
38
|
+
},
|
|
39
|
+
"prisma": {
|
|
40
|
+
"seed": "tsx prisma/seed.ts"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Install these dependencies (use npm):
|
|
46
|
+
```
|
|
47
|
+
# Dependencies
|
|
48
|
+
npm install fastify @fastify/cors @fastify/helmet @fastify/rate-limit @fastify/cookie @fastify/jwt
|
|
49
|
+
npm install @prisma/client zod dotenv pino pino-pretty ioredis bcryptjs uuid
|
|
50
|
+
|
|
51
|
+
# Dev dependencies
|
|
52
|
+
npm install -D typescript tsx tsc-alias prisma @types/node @types/bcryptjs @types/uuid eslint @eslint/js typescript-eslint
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
#### `tsconfig.json`
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"compilerOptions": {
|
|
59
|
+
"target": "ES2020",
|
|
60
|
+
"module": "NodeNext",
|
|
61
|
+
"moduleResolution": "NodeNext",
|
|
62
|
+
"strict": true,
|
|
63
|
+
"esModuleInterop": true,
|
|
64
|
+
"skipLibCheck": true,
|
|
65
|
+
"forceConsistentCasingInFileNames": true,
|
|
66
|
+
"resolveJsonModule": true,
|
|
67
|
+
"declaration": true,
|
|
68
|
+
"declarationMap": true,
|
|
69
|
+
"sourceMap": true,
|
|
70
|
+
"outDir": "dist",
|
|
71
|
+
"rootDir": "src",
|
|
72
|
+
"baseUrl": ".",
|
|
73
|
+
"paths": {
|
|
74
|
+
"@/*": ["./src/*"],
|
|
75
|
+
"@modules/*": ["./src/modules/*"],
|
|
76
|
+
"@libs/*": ["./src/libs/*"],
|
|
77
|
+
"@config/*": ["./src/config/*"],
|
|
78
|
+
"@shared/*": ["./src/shared/*"]
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"include": ["src/**/*"],
|
|
82
|
+
"exclude": ["node_modules", "dist"]
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
> **Note**: The `tsc-alias` package resolves path aliases at build time. `tsx` handles paths natively in development.
|
|
87
|
+
|
|
88
|
+
#### `eslint.config.mjs`
|
|
89
|
+
```js
|
|
90
|
+
import eslint from '@eslint/js';
|
|
91
|
+
import tseslint from 'typescript-eslint';
|
|
92
|
+
|
|
93
|
+
export default tseslint.config(
|
|
94
|
+
eslint.configs.recommended,
|
|
95
|
+
...tseslint.configs.recommended,
|
|
96
|
+
{
|
|
97
|
+
rules: {
|
|
98
|
+
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
|
99
|
+
'@typescript-eslint/explicit-function-return-type': 'warn',
|
|
100
|
+
'@typescript-eslint/no-explicit-any': 'error',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
ignores: ['dist/', 'node_modules/', 'prisma/'],
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### `.env` and `.env.example`
|
|
110
|
+
```env
|
|
111
|
+
# App
|
|
112
|
+
NODE_ENV=development
|
|
113
|
+
PORT=3000
|
|
114
|
+
HOST=0.0.0.0
|
|
115
|
+
|
|
116
|
+
# Database
|
|
117
|
+
DATABASE_URL="mysql://root:rootpassword@localhost:3306/$ARGUMENTS"
|
|
118
|
+
|
|
119
|
+
# Redis
|
|
120
|
+
REDIS_URL="redis://localhost:6379"
|
|
121
|
+
|
|
122
|
+
# JWT
|
|
123
|
+
JWT_SECRET="change-this-to-a-secure-random-string"
|
|
124
|
+
JWT_ACCESS_EXPIRY="15m"
|
|
125
|
+
JWT_REFRESH_EXPIRY="7d"
|
|
126
|
+
|
|
127
|
+
# CORS
|
|
128
|
+
CORS_ORIGIN="http://localhost:3001"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### `.gitignore`
|
|
132
|
+
Include: node_modules, dist, .env, .env.local, .env*.local, *.log, .prisma
|
|
133
|
+
|
|
134
|
+
#### `docker-compose.yml`
|
|
135
|
+
Create a Docker Compose file with these services:
|
|
136
|
+
1. **mysql** - MySQL 8.0, port 3306, database name = `$ARGUMENTS`, root password = `rootpassword`, volume for data persistence, healthcheck
|
|
137
|
+
2. **phpmyadmin** - Latest, port 8080, linked to mysql
|
|
138
|
+
3. **redis** - Redis 7 Alpine, port 6379, volume for data persistence, healthcheck
|
|
139
|
+
4. **redis-commander** - Redis Commander UI, port 8081, linked to redis
|
|
140
|
+
|
|
141
|
+
Use a named network for all services. Add restart policies.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
### Step 2: Source Code Structure
|
|
146
|
+
|
|
147
|
+
Create the following directory structure under `src/`:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
src/
|
|
151
|
+
├── app.ts # Fastify instance, plugin registration
|
|
152
|
+
├── server.ts # listen() call only
|
|
153
|
+
├── config/
|
|
154
|
+
│ └── env.ts # Zod-validated environment variables
|
|
155
|
+
├── libs/
|
|
156
|
+
│ ├── prisma.ts # Prisma client singleton
|
|
157
|
+
│ ├── redis.ts # Redis client singleton
|
|
158
|
+
│ ├── logger.ts # Pino logger
|
|
159
|
+
│ └── auth.ts # JWT helpers (sign, verify, middleware)
|
|
160
|
+
├── shared/
|
|
161
|
+
│ ├── errors/
|
|
162
|
+
│ │ ├── AppError.ts # Base error class
|
|
163
|
+
│ │ └── errors.ts # All typed error subclasses
|
|
164
|
+
│ ├── responses/
|
|
165
|
+
│ │ ├── successResponse.ts
|
|
166
|
+
│ │ └── paginatedResponse.ts
|
|
167
|
+
│ ├── schemas/
|
|
168
|
+
│ │ └── pagination.schema.ts # Shared Zod pagination schema
|
|
169
|
+
│ └── types/
|
|
170
|
+
│ └── index.ts # Shared TypeScript types
|
|
171
|
+
└── modules/
|
|
172
|
+
└── auth/ # Auth module
|
|
173
|
+
├── auth.routes.ts
|
|
174
|
+
├── auth.controller.ts
|
|
175
|
+
├── auth.service.ts
|
|
176
|
+
├── auth.repo.ts
|
|
177
|
+
└── auth.schemas.ts
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
### Step 3: Core Files Implementation
|
|
183
|
+
|
|
184
|
+
#### `src/config/env.ts`
|
|
185
|
+
- Use Zod to validate ALL environment variables at startup
|
|
186
|
+
- Export typed `env` object
|
|
187
|
+
- Crash immediately with clear message if validation fails
|
|
188
|
+
|
|
189
|
+
#### `src/libs/logger.ts`
|
|
190
|
+
- Use Pino logger
|
|
191
|
+
- Pretty print in development, JSON in production
|
|
192
|
+
- Export singleton `logger`
|
|
193
|
+
|
|
194
|
+
#### `src/libs/prisma.ts`
|
|
195
|
+
- Singleton Prisma client
|
|
196
|
+
- Handle graceful shutdown (disconnect on process exit)
|
|
197
|
+
|
|
198
|
+
#### `src/libs/redis.ts`
|
|
199
|
+
- Singleton ioredis client
|
|
200
|
+
- Configure from `REDIS_URL` env
|
|
201
|
+
- Handle connection errors with logger
|
|
202
|
+
- Handle graceful shutdown
|
|
203
|
+
|
|
204
|
+
#### `src/libs/auth.ts`
|
|
205
|
+
- JWT sign/verify using @fastify/jwt
|
|
206
|
+
- Fastify `authenticate` preHandler decorator
|
|
207
|
+
- Fastify `optionalAuth` preHandler decorator
|
|
208
|
+
- Role-based `authorize(...roles)` preHandler
|
|
209
|
+
|
|
210
|
+
#### `src/shared/errors/AppError.ts`
|
|
211
|
+
- Base `AppError` class extending `Error` with: `code`, `statusCode`, `message`
|
|
212
|
+
|
|
213
|
+
#### `src/shared/errors/errors.ts`
|
|
214
|
+
- Export all subclasses: `BadRequestError`, `ValidationError`, `UnauthorizedError`, `ForbiddenError`, `NotFoundError`, `ConflictError`, `InternalError`
|
|
215
|
+
- Each with appropriate default status code and code string
|
|
216
|
+
|
|
217
|
+
#### `src/shared/responses/successResponse.ts`
|
|
218
|
+
```typescript
|
|
219
|
+
export function successResponse<T>(message: string, data: T) {
|
|
220
|
+
return { success: true as const, message, data };
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
#### `src/shared/responses/paginatedResponse.ts`
|
|
225
|
+
```typescript
|
|
226
|
+
export function paginatedResponse<T>(
|
|
227
|
+
message: string,
|
|
228
|
+
items: T[],
|
|
229
|
+
page: number,
|
|
230
|
+
limit: number,
|
|
231
|
+
totalItems: number
|
|
232
|
+
) {
|
|
233
|
+
const totalPages = Math.ceil(totalItems / limit);
|
|
234
|
+
return {
|
|
235
|
+
success: true as const,
|
|
236
|
+
message,
|
|
237
|
+
data: {
|
|
238
|
+
items,
|
|
239
|
+
pagination: {
|
|
240
|
+
page,
|
|
241
|
+
limit,
|
|
242
|
+
totalItems,
|
|
243
|
+
totalPages,
|
|
244
|
+
hasNextPage: page < totalPages,
|
|
245
|
+
hasPreviousPage: page > 1,
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
#### `src/shared/schemas/pagination.schema.ts`
|
|
253
|
+
```typescript
|
|
254
|
+
import { z } from 'zod';
|
|
255
|
+
|
|
256
|
+
export const PaginationSchema = z.object({
|
|
257
|
+
page: z.coerce.number().int().min(1).default(1),
|
|
258
|
+
limit: z.coerce.number().int().min(1).max(100).default(10),
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
export type PaginationInput = z.infer<typeof PaginationSchema>;
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
#### `src/app.ts`
|
|
265
|
+
- Create and configure Fastify instance
|
|
266
|
+
- Register plugins: cors, helmet, rate-limit, cookie, jwt
|
|
267
|
+
- Register health check route: `GET /api/v1/health` — returns `successResponse('Server is healthy', { status: 'ok', timestamp: new Date().toISOString() })`
|
|
268
|
+
- Register global error handler that maps `AppError` → proper response format
|
|
269
|
+
- Register routes with `/api/v1` prefix
|
|
270
|
+
- Export the app instance
|
|
271
|
+
|
|
272
|
+
The global error handler MUST:
|
|
273
|
+
- Check if error is `AppError` → use its code, message, statusCode
|
|
274
|
+
- Check if error is Zod validation error → 422 with validation details
|
|
275
|
+
- Check if error is Fastify validation error → 400
|
|
276
|
+
- Default to 500 Internal Server Error
|
|
277
|
+
- Log internal errors with logger
|
|
278
|
+
- NEVER expose stack traces or internal details to client
|
|
279
|
+
|
|
280
|
+
#### `src/server.ts`
|
|
281
|
+
- Import app from `app.ts`
|
|
282
|
+
- Import env config
|
|
283
|
+
- Call `app.listen({ port, host })`
|
|
284
|
+
- Log startup message with port and environment
|
|
285
|
+
- Handle graceful shutdown (SIGINT, SIGTERM) — close Fastify, disconnect Prisma, disconnect Redis
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
### Step 4: Auth Module
|
|
290
|
+
|
|
291
|
+
Create a working auth module with these endpoints:
|
|
292
|
+
- `POST /api/v1/auth/register` - Register new user
|
|
293
|
+
- `POST /api/v1/auth/login` - Login with email/password
|
|
294
|
+
- `POST /api/v1/auth/logout` - Logout (invalidate refresh token)
|
|
295
|
+
- `POST /api/v1/auth/refresh` - Refresh access token
|
|
296
|
+
- `GET /api/v1/auth/me` - Get current user (protected)
|
|
297
|
+
|
|
298
|
+
Follow the layered architecture:
|
|
299
|
+
- **auth.schemas.ts** - Zod schemas for register, login, refresh input
|
|
300
|
+
- **auth.controller.ts** - Validate input, call service, return `successResponse()`
|
|
301
|
+
- **auth.service.ts** - Business logic, bcrypt hashing, JWT token generation, throw typed errors
|
|
302
|
+
- **auth.repo.ts** - Prisma queries for users and refresh tokens
|
|
303
|
+
- **auth.routes.ts** - Fastify plugin registering routes with appropriate preHandlers
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
### Step 5: Prisma Setup
|
|
308
|
+
|
|
309
|
+
#### `prisma/schema.prisma`
|
|
310
|
+
```prisma
|
|
311
|
+
generator client {
|
|
312
|
+
provider = "prisma-client-js"
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
datasource db {
|
|
316
|
+
provider = "mysql"
|
|
317
|
+
url = env("DATABASE_URL")
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
model User {
|
|
321
|
+
id String @id @default(uuid())
|
|
322
|
+
email String @unique
|
|
323
|
+
password String
|
|
324
|
+
firstName String
|
|
325
|
+
lastName String
|
|
326
|
+
role String @default("USER")
|
|
327
|
+
isActive Boolean @default(true)
|
|
328
|
+
emailVerified Boolean @default(false)
|
|
329
|
+
deletedAt DateTime?
|
|
330
|
+
createdAt DateTime @default(now())
|
|
331
|
+
updatedAt DateTime @updatedAt
|
|
332
|
+
|
|
333
|
+
refreshTokens RefreshToken[]
|
|
334
|
+
|
|
335
|
+
@@map("users")
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
model RefreshToken {
|
|
339
|
+
id String @id @default(uuid())
|
|
340
|
+
token String @unique @db.VarChar(500)
|
|
341
|
+
userId String
|
|
342
|
+
expiresAt DateTime
|
|
343
|
+
createdAt DateTime @default(now())
|
|
344
|
+
|
|
345
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
346
|
+
|
|
347
|
+
@@index([userId])
|
|
348
|
+
@@index([token])
|
|
349
|
+
@@map("refresh_tokens")
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
#### `prisma/seed.ts`
|
|
354
|
+
- Create a basic seed file that creates an admin user and a test user
|
|
355
|
+
- Use bcrypt to hash passwords
|
|
356
|
+
- Wrap in try/catch with proper logging
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
### Step 6: Final Verification
|
|
361
|
+
|
|
362
|
+
After creating all files:
|
|
363
|
+
1. Run `npm install` to install dependencies
|
|
364
|
+
2. Run `docker compose up -d` to start services (if Docker is available, otherwise remind the user)
|
|
365
|
+
3. Wait for MySQL to be healthy, then run `npx prisma migrate dev --name init`
|
|
366
|
+
4. Run `npx prisma generate`
|
|
367
|
+
5. Run `npx prisma db seed`
|
|
368
|
+
6. Verify the project starts with `npm run dev`
|
|
369
|
+
7. Test health check: `curl http://localhost:3000/api/v1/health`
|
|
370
|
+
|
|
371
|
+
If Docker is not running, inform the user they need to run `docker compose up -d` before running migrations.
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## IMPORTANT RULES
|
|
376
|
+
|
|
377
|
+
- Follow ALL rules from `.claude/rules/server/` and `.claude/rules/global/`
|
|
378
|
+
- Use the EXACT response format from `response-handling.md` (includes pagination contract)
|
|
379
|
+
- All routes prefixed with `/api/v1`
|
|
380
|
+
- TypeScript strict mode
|
|
381
|
+
- No `console.log` — use logger
|
|
382
|
+
- No raw `Error` throws — use AppError subclasses
|
|
383
|
+
- Named exports everywhere except `app.ts`
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
> **SCOPE**: These rules apply specifically to the **client** directory (Next.js App Router).
|
|
2
|
+
|
|
3
|
+
# Project Structure
|
|
4
|
+
|
|
5
|
+
## Folder Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
src/
|
|
9
|
+
├── app/ # Next.js App Router
|
|
10
|
+
│ ├── layout.tsx # Root layout
|
|
11
|
+
│ ├── page.tsx # Home page
|
|
12
|
+
│ ├── providers.tsx # Client providers (Redux, React Query)
|
|
13
|
+
│ ├── globals.css # Global styles
|
|
14
|
+
│ ├── (auth)/ # Auth route group
|
|
15
|
+
│ │ ├── login/page.tsx
|
|
16
|
+
│ │ ├── register/page.tsx
|
|
17
|
+
│ │ └── layout.tsx
|
|
18
|
+
│ ├── (main)/ # Main route group
|
|
19
|
+
│ │ ├── layout.tsx # Header/Footer layout
|
|
20
|
+
│ │ └── <domain>/ # Domain-specific routes
|
|
21
|
+
│ ├── dashboard/ # Protected routes
|
|
22
|
+
│ └── admin/ # Admin routes
|
|
23
|
+
├── components/
|
|
24
|
+
│ ├── ui/ # shadcn/ui components
|
|
25
|
+
│ ├── layout/ # Header, Footer, Sidebar, MainLayout
|
|
26
|
+
│ └── common/ # LoadingSpinner, ErrorBoundary, Pagination, EmptyState
|
|
27
|
+
├── features/ # Feature modules (domain-driven)
|
|
28
|
+
│ └── <domain>/
|
|
29
|
+
│ ├── components/
|
|
30
|
+
│ ├── hooks/
|
|
31
|
+
│ ├── services/ # <domain>.service.ts
|
|
32
|
+
│ ├── store/ # <domain>Slice.ts (if needed)
|
|
33
|
+
│ ├── types/ # <domain>.types.ts
|
|
34
|
+
│ └── actions/ # <domain>.actions.ts (Server Actions)
|
|
35
|
+
├── hooks/ # Global hooks (useDebounce, useLocalStorage, useMediaQuery)
|
|
36
|
+
├── lib/
|
|
37
|
+
│ ├── api/ # axios.config.ts, api.types.ts
|
|
38
|
+
│ ├── constants/ # routes.ts, api-endpoints.ts, app.constants.ts
|
|
39
|
+
│ └── utils/ # format.ts, validation.ts, error.ts, cn() helper
|
|
40
|
+
├── store/ # Redux store (index.ts, hooks.ts)
|
|
41
|
+
├── types/ # Global types
|
|
42
|
+
└── middleware.ts # Auth route protection
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## File Naming
|
|
46
|
+
|
|
47
|
+
| Type | Pattern | Example |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| Component | `PascalCase.tsx` | `ProductCard.tsx` |
|
|
50
|
+
| Page | `folder/page.tsx` | `products/page.tsx` |
|
|
51
|
+
| Hook | `use<Name>.ts` | `useAuth.ts` |
|
|
52
|
+
| Service | `<domain>.service.ts` | `product.service.ts` |
|
|
53
|
+
| Types | `<domain>.types.ts` | `product.types.ts` |
|
|
54
|
+
| Redux slice | `<domain>Slice.ts` | `authSlice.ts` |
|
|
55
|
+
| Server Action | `<domain>.actions.ts` | `product.actions.ts` |
|
|
56
|
+
| Page exports | `default export` | Required by Next.js |
|
|
57
|
+
| Everything else | Named exports | `export const ProductCard` |
|
|
58
|
+
|
|
59
|
+
## Import Order
|
|
60
|
+
|
|
61
|
+
1. React / Next.js (`useState`, `useRouter`, `Image`, `Link`)
|
|
62
|
+
2. Third-party (`@tanstack/react-query`, `sonner`)
|
|
63
|
+
3. UI components (`@/components/ui/*`)
|
|
64
|
+
4. Local components
|
|
65
|
+
5. Hooks
|
|
66
|
+
6. Services
|
|
67
|
+
7. Types (always `import type`)
|
|
68
|
+
8. Utils (`cn`, `formatDate`)
|
|
69
|
+
|
|
70
|
+
## Constants
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// lib/constants/api-endpoints.ts
|
|
74
|
+
export const API_ENDPOINTS = {
|
|
75
|
+
AUTH: {
|
|
76
|
+
REGISTER: '/auth/register',
|
|
77
|
+
LOGIN: '/auth/login',
|
|
78
|
+
LOGOUT: '/auth/logout',
|
|
79
|
+
REFRESH: '/auth/refresh',
|
|
80
|
+
ME: '/auth/me',
|
|
81
|
+
VERIFY_EMAIL: '/auth/verify-email',
|
|
82
|
+
RESEND_VERIFICATION: '/auth/resend-verification',
|
|
83
|
+
REQUEST_PASSWORD_RESET: '/auth/request-password-reset',
|
|
84
|
+
RESET_PASSWORD: '/auth/reset-password',
|
|
85
|
+
},
|
|
86
|
+
USERS: {
|
|
87
|
+
ME: '/users/me',
|
|
88
|
+
UPDATE_ME: '/users/me',
|
|
89
|
+
DELETE_ME: '/users/me',
|
|
90
|
+
},
|
|
91
|
+
// Add domain-specific endpoints following this pattern:
|
|
92
|
+
// <DOMAIN>: {
|
|
93
|
+
// LIST: '/<domain>',
|
|
94
|
+
// CREATE: '/<domain>',
|
|
95
|
+
// GET: (id: string) => `/<domain>/${id}`,
|
|
96
|
+
// UPDATE: (id: string) => `/<domain>/${id}`,
|
|
97
|
+
// DELETE: (id: string) => `/<domain>/${id}`,
|
|
98
|
+
// },
|
|
99
|
+
} as const;
|
|
100
|
+
|
|
101
|
+
// lib/constants/routes.ts
|
|
102
|
+
export const ROUTES = {
|
|
103
|
+
HOME: '/',
|
|
104
|
+
LOGIN: '/login',
|
|
105
|
+
REGISTER: '/register',
|
|
106
|
+
VERIFY_EMAIL: '/verify-email',
|
|
107
|
+
RESET_PASSWORD: '/reset-password',
|
|
108
|
+
DASHBOARD: '/dashboard',
|
|
109
|
+
PROFILE: '/profile',
|
|
110
|
+
// Add domain-specific routes following this pattern:
|
|
111
|
+
// <DOMAIN>: {
|
|
112
|
+
// LIST: '/<domain>',
|
|
113
|
+
// DETAILS: (id: string) => `/<domain>/${id}`,
|
|
114
|
+
// CREATE: '/<domain>/create',
|
|
115
|
+
// EDIT: (id: string) => `/<domain>/${id}/edit`,
|
|
116
|
+
// },
|
|
117
|
+
} as const;
|
|
118
|
+
|
|
119
|
+
// lib/constants/app.constants.ts
|
|
120
|
+
export const APP_NAME = 'My App';
|
|
121
|
+
export const PAGINATION = { DEFAULT_PAGE: 1, DEFAULT_LIMIT: 10, MAX_LIMIT: 100 } as const;
|
|
122
|
+
export const USER_ROLES = { USER: 'USER', ADMIN: 'ADMIN' } as const;
|
|
123
|
+
export const CURRENCIES = { USD: 'USD', EUR: 'EUR' } as const;
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## App Providers
|
|
127
|
+
|
|
128
|
+
`app/providers.tsx` wraps the app with: **ReduxProvider** (store) → **QueryClientProvider** (React Query) → **Toaster** (sonner, position: top-right).
|
|
129
|
+
|
|
130
|
+
## Middleware
|
|
131
|
+
|
|
132
|
+
Protected paths: `/dashboard`, `/profile`, `/admin` — redirect to `/login` if no token.
|
|
133
|
+
Auth paths: `/login`, `/register` — redirect to `/dashboard` if already authenticated.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
> **SCOPE**: These rules apply specifically to the **client** directory (Next.js App Router).
|
|
2
|
+
|
|
3
|
+
# Components & Types
|
|
4
|
+
|
|
5
|
+
## Component Rules
|
|
6
|
+
|
|
7
|
+
- **Server Components by default.** Only add `'use client'` for hooks, state, or event handlers.
|
|
8
|
+
- **Max 250 lines** per component. Split if exceeded.
|
|
9
|
+
- **Max 5 props.** Group related props into an object if more are needed.
|
|
10
|
+
- **Max 3 levels** of JSX nesting. Extract sub-components if deeper.
|
|
11
|
+
- **Internal ordering**: hooks → event handlers → effects → early returns → render.
|
|
12
|
+
- **Handler naming**: `handle<Event>` for internal handlers, `on<Event>` for callback props.
|
|
13
|
+
- **Wrap handlers in `useCallback`** when passed to child components (Client Components only).
|
|
14
|
+
- **Use `useMemo`** for expensive computations in Client Components.
|
|
15
|
+
|
|
16
|
+
### Anti-patterns
|
|
17
|
+
|
|
18
|
+
- Do NOT add `'use client'` to components that have no interactivity — keep them as Server Components.
|
|
19
|
+
- Do NOT fetch data in Client Components when a Server Component can fetch it and pass as props.
|
|
20
|
+
- Do NOT use inline styles. Use Tailwind + `cn()` for conditional classes.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## TypeScript Rules
|
|
25
|
+
|
|
26
|
+
- **Strict mode ON.** No `any` — use `unknown` if the type is truly unknown.
|
|
27
|
+
- **Explicit return types** on all functions.
|
|
28
|
+
- **`interface`** for component props and object shapes. **`type`** for unions, intersections, utilities.
|
|
29
|
+
- **Prefer string unions** over enums: `type UserRole = 'USER' | 'ADMIN'`
|
|
30
|
+
- **Infer form types from Zod**: `type FormData = z.infer<typeof formSchema>`
|
|
31
|
+
- **Always use `import type`** for type-only imports.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## API Response Types
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// lib/api/api.types.ts
|
|
39
|
+
|
|
40
|
+
export interface ApiResponse<T> {
|
|
41
|
+
success: boolean;
|
|
42
|
+
message: string;
|
|
43
|
+
data: T;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface PaginatedApiResponse<T> {
|
|
47
|
+
success: boolean;
|
|
48
|
+
message: string;
|
|
49
|
+
data: {
|
|
50
|
+
items: T[];
|
|
51
|
+
pagination: {
|
|
52
|
+
page: number;
|
|
53
|
+
limit: number;
|
|
54
|
+
totalItems: number;
|
|
55
|
+
totalPages: number;
|
|
56
|
+
hasNextPage: boolean;
|
|
57
|
+
hasPreviousPage: boolean;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ApiError {
|
|
63
|
+
success: false;
|
|
64
|
+
error: {
|
|
65
|
+
code: string;
|
|
66
|
+
message: string;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface PaginationParams {
|
|
71
|
+
page?: number;
|
|
72
|
+
limit?: number;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Domain Types
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// features/auth/types/auth.types.ts
|
|
82
|
+
|
|
83
|
+
export interface IUser {
|
|
84
|
+
id: string;
|
|
85
|
+
email: string;
|
|
86
|
+
firstName: string;
|
|
87
|
+
lastName: string;
|
|
88
|
+
role: UserRole;
|
|
89
|
+
isActive: boolean;
|
|
90
|
+
avatarUrl: string | null;
|
|
91
|
+
createdAt: string;
|
|
92
|
+
updatedAt: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type UserRole = 'USER' | 'ADMIN';
|
|
96
|
+
|
|
97
|
+
export interface ILoginRequest {
|
|
98
|
+
email: string;
|
|
99
|
+
password: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface IRegisterRequest {
|
|
103
|
+
email: string;
|
|
104
|
+
password: string;
|
|
105
|
+
firstName: string;
|
|
106
|
+
lastName: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// No IAuthTokens — tokens are stored in httpOnly cookies, never exposed to JS.
|
|
110
|
+
|
|
111
|
+
export interface IAuthState {
|
|
112
|
+
user: IUser | null;
|
|
113
|
+
isAuthenticated: boolean;
|
|
114
|
+
isInitializing: boolean; // true until first getMe() resolves
|
|
115
|
+
isLoggingOut: boolean; // true during logout API call
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// features/<domain>/types/<domain>.types.ts — example pattern
|
|
121
|
+
|
|
122
|
+
export interface Item {
|
|
123
|
+
id: string;
|
|
124
|
+
ownerId: string;
|
|
125
|
+
title: string;
|
|
126
|
+
description: string | null;
|
|
127
|
+
isActive: boolean;
|
|
128
|
+
createdAt: string;
|
|
129
|
+
updatedAt: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface CreateItemRequest {
|
|
133
|
+
title: string;
|
|
134
|
+
description?: string;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface UpdateItemRequest {
|
|
138
|
+
title?: string;
|
|
139
|
+
description?: string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface ItemFilters {
|
|
143
|
+
search?: string;
|
|
144
|
+
sortBy?: 'createdAt' | 'title';
|
|
145
|
+
}
|
|
146
|
+
```
|