@malamute/ai-rules 1.0.0 → 1.2.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/README.md +270 -121
- package/bin/cli.js +5 -2
- package/configs/_shared/.claude/rules/conventions/documentation.md +324 -0
- package/configs/_shared/.claude/rules/conventions/git.md +265 -0
- package/configs/_shared/.claude/rules/{performance.md → conventions/performance.md} +1 -1
- package/configs/_shared/.claude/rules/conventions/principles.md +334 -0
- package/configs/_shared/.claude/rules/devops/ci-cd.md +262 -0
- package/configs/_shared/.claude/rules/devops/docker.md +275 -0
- package/configs/_shared/.claude/rules/devops/nx.md +194 -0
- package/configs/_shared/.claude/rules/domain/backend/api-design.md +203 -0
- package/configs/_shared/.claude/rules/lang/csharp/async.md +220 -0
- package/configs/_shared/.claude/rules/lang/csharp/csharp.md +314 -0
- package/configs/_shared/.claude/rules/lang/csharp/linq.md +210 -0
- package/configs/_shared/.claude/rules/lang/python/async.md +337 -0
- package/configs/_shared/.claude/rules/lang/python/celery.md +476 -0
- package/configs/_shared/.claude/rules/lang/python/config.md +339 -0
- package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/database/sqlalchemy.md +6 -1
- package/configs/_shared/.claude/rules/lang/python/deployment.md +523 -0
- package/configs/_shared/.claude/rules/lang/python/error-handling.md +330 -0
- package/configs/_shared/.claude/rules/lang/python/migrations.md +421 -0
- package/configs/_shared/.claude/rules/lang/python/python.md +172 -0
- package/configs/_shared/.claude/rules/lang/python/repository.md +383 -0
- package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/testing.md +2 -69
- package/configs/_shared/.claude/rules/lang/typescript/async.md +447 -0
- package/configs/_shared/.claude/rules/lang/typescript/generics.md +356 -0
- package/configs/_shared/.claude/rules/lang/typescript/typescript.md +212 -0
- package/configs/_shared/.claude/rules/quality/error-handling.md +48 -0
- package/configs/_shared/.claude/rules/quality/logging.md +45 -0
- package/configs/_shared/.claude/rules/quality/observability.md +240 -0
- package/configs/_shared/.claude/rules/quality/testing-patterns.md +65 -0
- package/configs/_shared/.claude/rules/security/secrets-management.md +222 -0
- package/configs/_shared/.claude/skills/analysis/explore/SKILL.md +257 -0
- package/configs/_shared/.claude/skills/analysis/security-audit/SKILL.md +184 -0
- package/configs/_shared/.claude/skills/dev/api-endpoint/SKILL.md +126 -0
- package/configs/_shared/.claude/{commands/generate-tests.md → skills/dev/generate-tests/SKILL.md} +6 -0
- package/configs/_shared/.claude/{commands/fix-issue.md → skills/git/fix-issue/SKILL.md} +6 -0
- package/configs/_shared/.claude/{commands/review-pr.md → skills/git/review-pr/SKILL.md} +6 -0
- package/configs/_shared/.claude/skills/infra/deploy/SKILL.md +139 -0
- package/configs/_shared/.claude/skills/infra/docker/SKILL.md +95 -0
- package/configs/_shared/.claude/skills/infra/migration/SKILL.md +158 -0
- package/configs/_shared/.claude/skills/nx/nx-affected/SKILL.md +72 -0
- package/configs/_shared/.claude/skills/nx/nx-lib/SKILL.md +375 -0
- package/configs/_shared/CLAUDE.md +52 -149
- package/configs/angular/.claude/rules/{components.md → core/components.md} +69 -15
- package/configs/angular/.claude/rules/core/resource.md +285 -0
- package/configs/angular/.claude/rules/core/signals.md +323 -0
- package/configs/angular/.claude/rules/http.md +338 -0
- package/configs/angular/.claude/rules/routing.md +291 -0
- package/configs/angular/.claude/rules/ssr.md +312 -0
- package/configs/angular/.claude/rules/state/signal-store.md +408 -0
- package/configs/angular/.claude/rules/{state.md → state/state.md} +2 -2
- package/configs/angular/.claude/rules/testing.md +7 -7
- package/configs/angular/.claude/rules/ui/aria.md +422 -0
- package/configs/angular/.claude/rules/ui/forms.md +424 -0
- package/configs/angular/.claude/rules/ui/pipes-directives.md +335 -0
- package/configs/angular/.claude/settings.json +1 -0
- package/configs/angular/.claude/skills/ngrx-slice/SKILL.md +362 -0
- package/configs/angular/.claude/skills/signal-store/SKILL.md +445 -0
- package/configs/angular/CLAUDE.md +24 -216
- package/configs/dotnet/.claude/rules/background-services.md +552 -0
- package/configs/dotnet/.claude/rules/configuration.md +426 -0
- package/configs/dotnet/.claude/rules/ddd.md +447 -0
- package/configs/dotnet/.claude/rules/dependency-injection.md +343 -0
- package/configs/dotnet/.claude/rules/mediatr.md +320 -0
- package/configs/dotnet/.claude/rules/middleware.md +489 -0
- package/configs/dotnet/.claude/rules/result-pattern.md +363 -0
- package/configs/dotnet/.claude/rules/validation.md +388 -0
- package/configs/dotnet/.claude/settings.json +21 -3
- package/configs/dotnet/CLAUDE.md +53 -286
- package/configs/fastapi/.claude/rules/background-tasks.md +254 -0
- package/configs/fastapi/.claude/rules/dependencies.md +170 -0
- package/configs/{python → fastapi}/.claude/rules/fastapi.md +61 -1
- package/configs/fastapi/.claude/rules/lifespan.md +274 -0
- package/configs/fastapi/.claude/rules/middleware.md +229 -0
- package/configs/fastapi/.claude/rules/pydantic.md +433 -0
- package/configs/fastapi/.claude/rules/responses.md +251 -0
- package/configs/fastapi/.claude/rules/routers.md +202 -0
- package/configs/fastapi/.claude/rules/security.md +222 -0
- package/configs/fastapi/.claude/rules/testing.md +251 -0
- package/configs/fastapi/.claude/rules/websockets.md +298 -0
- package/configs/fastapi/.claude/settings.json +33 -0
- package/configs/fastapi/CLAUDE.md +144 -0
- package/configs/flask/.claude/rules/blueprints.md +208 -0
- package/configs/flask/.claude/rules/cli.md +285 -0
- package/configs/flask/.claude/rules/configuration.md +281 -0
- package/configs/flask/.claude/rules/context.md +238 -0
- package/configs/flask/.claude/rules/error-handlers.md +278 -0
- package/configs/flask/.claude/rules/extensions.md +278 -0
- package/configs/flask/.claude/rules/flask.md +171 -0
- package/configs/flask/.claude/rules/marshmallow.md +206 -0
- package/configs/flask/.claude/rules/security.md +267 -0
- package/configs/flask/.claude/rules/testing.md +284 -0
- package/configs/flask/.claude/settings.json +33 -0
- package/configs/flask/CLAUDE.md +166 -0
- package/configs/nestjs/.claude/rules/common-patterns.md +300 -0
- package/configs/nestjs/.claude/rules/filters.md +376 -0
- package/configs/nestjs/.claude/rules/interceptors.md +317 -0
- package/configs/nestjs/.claude/rules/middleware.md +321 -0
- package/configs/nestjs/.claude/rules/modules.md +26 -0
- package/configs/nestjs/.claude/rules/pipes.md +351 -0
- package/configs/nestjs/.claude/rules/websockets.md +451 -0
- package/configs/nestjs/.claude/settings.json +16 -2
- package/configs/nestjs/CLAUDE.md +57 -215
- package/configs/nextjs/.claude/rules/api-routes.md +358 -0
- package/configs/nextjs/.claude/rules/authentication.md +355 -0
- package/configs/nextjs/.claude/rules/components.md +52 -0
- package/configs/nextjs/.claude/rules/data-fetching.md +249 -0
- package/configs/nextjs/.claude/rules/database.md +400 -0
- package/configs/nextjs/.claude/rules/middleware.md +303 -0
- package/configs/nextjs/.claude/rules/routing.md +324 -0
- package/configs/nextjs/.claude/rules/seo.md +350 -0
- package/configs/nextjs/.claude/rules/server-actions.md +353 -0
- package/configs/nextjs/.claude/rules/state/zustand.md +6 -6
- package/configs/nextjs/.claude/settings.json +5 -0
- package/configs/nextjs/CLAUDE.md +69 -331
- package/package.json +23 -9
- package/src/cli.js +220 -0
- package/src/config.js +29 -0
- package/src/index.js +13 -0
- package/src/installer.js +361 -0
- package/src/merge.js +116 -0
- package/src/tech-config.json +29 -0
- package/src/utils.js +96 -0
- package/configs/python/.claude/rules/flask.md +0 -332
- package/configs/python/.claude/settings.json +0 -18
- package/configs/python/CLAUDE.md +0 -273
- package/src/install.js +0 -315
- /package/configs/_shared/.claude/rules/{accessibility.md → domain/frontend/accessibility.md} +0 -0
- /package/configs/_shared/.claude/rules/{security.md → security/security.md} +0 -0
- /package/configs/_shared/.claude/skills/{debug → dev/debug}/SKILL.md +0 -0
- /package/configs/_shared/.claude/skills/{learning → dev/learning}/SKILL.md +0 -0
- /package/configs/_shared/.claude/skills/{spec → dev/spec}/SKILL.md +0 -0
- /package/configs/_shared/.claude/skills/{review → git/review}/SKILL.md +0 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/Dockerfile"
|
|
4
|
+
- "**/docker-compose*.yml"
|
|
5
|
+
- "**/.dockerignore"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Docker Best Practices
|
|
9
|
+
|
|
10
|
+
## Dockerfile Principles
|
|
11
|
+
|
|
12
|
+
### Multi-stage Builds
|
|
13
|
+
|
|
14
|
+
Always use multi-stage builds to minimize image size:
|
|
15
|
+
|
|
16
|
+
```dockerfile
|
|
17
|
+
# Stage 1: Build
|
|
18
|
+
FROM node:20-alpine AS builder
|
|
19
|
+
WORKDIR /app
|
|
20
|
+
COPY package*.json ./
|
|
21
|
+
RUN npm ci
|
|
22
|
+
COPY . .
|
|
23
|
+
RUN npm run build
|
|
24
|
+
|
|
25
|
+
# Stage 2: Production
|
|
26
|
+
FROM node:20-alpine AS runner
|
|
27
|
+
WORKDIR /app
|
|
28
|
+
ENV NODE_ENV=production
|
|
29
|
+
COPY --from=builder /app/dist ./dist
|
|
30
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
31
|
+
USER node
|
|
32
|
+
EXPOSE 3000
|
|
33
|
+
CMD ["node", "dist/main.js"]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Layer Optimization
|
|
37
|
+
|
|
38
|
+
Order from least to most frequently changed:
|
|
39
|
+
|
|
40
|
+
```dockerfile
|
|
41
|
+
# 1. Base image (rarely changes)
|
|
42
|
+
FROM node:20-alpine
|
|
43
|
+
|
|
44
|
+
# 2. System dependencies (rarely changes)
|
|
45
|
+
RUN apk add --no-cache dumb-init
|
|
46
|
+
|
|
47
|
+
# 3. App dependencies (changes with package.json)
|
|
48
|
+
WORKDIR /app
|
|
49
|
+
COPY package*.json ./
|
|
50
|
+
RUN npm ci --only=production
|
|
51
|
+
|
|
52
|
+
# 4. App code (changes frequently)
|
|
53
|
+
COPY . .
|
|
54
|
+
|
|
55
|
+
# 5. Build (if needed)
|
|
56
|
+
RUN npm run build
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Security
|
|
60
|
+
|
|
61
|
+
```dockerfile
|
|
62
|
+
# Use specific version, not :latest
|
|
63
|
+
FROM node:20.10.0-alpine
|
|
64
|
+
|
|
65
|
+
# Run as non-root user
|
|
66
|
+
RUN addgroup -g 1001 appgroup && \
|
|
67
|
+
adduser -u 1001 -G appgroup -s /bin/sh -D appuser
|
|
68
|
+
USER appuser
|
|
69
|
+
|
|
70
|
+
# Don't expose unnecessary ports
|
|
71
|
+
EXPOSE 3000
|
|
72
|
+
|
|
73
|
+
# Use read-only filesystem where possible
|
|
74
|
+
# (set in docker-compose or runtime)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## .dockerignore
|
|
78
|
+
|
|
79
|
+
Always include:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
node_modules
|
|
83
|
+
npm-debug.log
|
|
84
|
+
.git
|
|
85
|
+
.gitignore
|
|
86
|
+
.env
|
|
87
|
+
.env.*
|
|
88
|
+
*.md
|
|
89
|
+
.vscode
|
|
90
|
+
.idea
|
|
91
|
+
coverage
|
|
92
|
+
dist
|
|
93
|
+
.nx
|
|
94
|
+
tmp
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Docker Compose
|
|
98
|
+
|
|
99
|
+
### Development
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
# docker-compose.yml
|
|
103
|
+
services:
|
|
104
|
+
app:
|
|
105
|
+
build:
|
|
106
|
+
context: .
|
|
107
|
+
dockerfile: Dockerfile
|
|
108
|
+
target: development
|
|
109
|
+
volumes:
|
|
110
|
+
- .:/app
|
|
111
|
+
- /app/node_modules # Prevent overwriting
|
|
112
|
+
ports:
|
|
113
|
+
- "3000:3000"
|
|
114
|
+
environment:
|
|
115
|
+
- NODE_ENV=development
|
|
116
|
+
depends_on:
|
|
117
|
+
- db
|
|
118
|
+
- redis
|
|
119
|
+
|
|
120
|
+
db:
|
|
121
|
+
image: postgres:16-alpine
|
|
122
|
+
environment:
|
|
123
|
+
POSTGRES_USER: dev
|
|
124
|
+
POSTGRES_PASSWORD: dev
|
|
125
|
+
POSTGRES_DB: app_dev
|
|
126
|
+
volumes:
|
|
127
|
+
- postgres_data:/var/lib/postgresql/data
|
|
128
|
+
ports:
|
|
129
|
+
- "5432:5432"
|
|
130
|
+
|
|
131
|
+
redis:
|
|
132
|
+
image: redis:7-alpine
|
|
133
|
+
ports:
|
|
134
|
+
- "6379:6379"
|
|
135
|
+
|
|
136
|
+
volumes:
|
|
137
|
+
postgres_data:
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Production
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
# docker-compose.prod.yml
|
|
144
|
+
services:
|
|
145
|
+
app:
|
|
146
|
+
build:
|
|
147
|
+
context: .
|
|
148
|
+
dockerfile: Dockerfile
|
|
149
|
+
target: production
|
|
150
|
+
restart: unless-stopped
|
|
151
|
+
read_only: true
|
|
152
|
+
security_opt:
|
|
153
|
+
- no-new-privileges:true
|
|
154
|
+
environment:
|
|
155
|
+
- NODE_ENV=production
|
|
156
|
+
healthcheck:
|
|
157
|
+
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
|
158
|
+
interval: 30s
|
|
159
|
+
timeout: 10s
|
|
160
|
+
retries: 3
|
|
161
|
+
start_period: 40s
|
|
162
|
+
deploy:
|
|
163
|
+
resources:
|
|
164
|
+
limits:
|
|
165
|
+
cpus: '1'
|
|
166
|
+
memory: 512M
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Framework-Specific Patterns
|
|
170
|
+
|
|
171
|
+
### Node.js / NestJS / Next.js
|
|
172
|
+
|
|
173
|
+
```dockerfile
|
|
174
|
+
FROM node:20-alpine AS base
|
|
175
|
+
RUN apk add --no-cache libc6-compat
|
|
176
|
+
WORKDIR /app
|
|
177
|
+
|
|
178
|
+
FROM base AS deps
|
|
179
|
+
COPY package*.json ./
|
|
180
|
+
RUN npm ci
|
|
181
|
+
|
|
182
|
+
FROM base AS builder
|
|
183
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
184
|
+
COPY . .
|
|
185
|
+
RUN npm run build
|
|
186
|
+
|
|
187
|
+
FROM base AS runner
|
|
188
|
+
ENV NODE_ENV=production
|
|
189
|
+
RUN addgroup --system --gid 1001 nodejs
|
|
190
|
+
RUN adduser --system --uid 1001 app
|
|
191
|
+
COPY --from=builder --chown=app:nodejs /app/dist ./dist
|
|
192
|
+
COPY --from=builder --chown=app:nodejs /app/node_modules ./node_modules
|
|
193
|
+
USER app
|
|
194
|
+
EXPOSE 3000
|
|
195
|
+
CMD ["node", "dist/main.js"]
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Python / FastAPI
|
|
199
|
+
|
|
200
|
+
```dockerfile
|
|
201
|
+
FROM python:3.12-slim AS base
|
|
202
|
+
ENV PYTHONDONTWRITEBYTECODE=1 \
|
|
203
|
+
PYTHONUNBUFFERED=1 \
|
|
204
|
+
PIP_NO_CACHE_DIR=1
|
|
205
|
+
|
|
206
|
+
FROM base AS builder
|
|
207
|
+
RUN pip install poetry
|
|
208
|
+
WORKDIR /app
|
|
209
|
+
COPY pyproject.toml poetry.lock ./
|
|
210
|
+
RUN poetry export -f requirements.txt --output requirements.txt
|
|
211
|
+
|
|
212
|
+
FROM base AS runner
|
|
213
|
+
WORKDIR /app
|
|
214
|
+
COPY --from=builder /app/requirements.txt .
|
|
215
|
+
RUN pip install -r requirements.txt
|
|
216
|
+
COPY . .
|
|
217
|
+
RUN useradd -m appuser && chown -R appuser:appuser /app
|
|
218
|
+
USER appuser
|
|
219
|
+
EXPOSE 8000
|
|
220
|
+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### .NET
|
|
224
|
+
|
|
225
|
+
```dockerfile
|
|
226
|
+
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
|
227
|
+
WORKDIR /src
|
|
228
|
+
COPY *.csproj ./
|
|
229
|
+
RUN dotnet restore
|
|
230
|
+
COPY . .
|
|
231
|
+
RUN dotnet publish -c Release -o /app/publish
|
|
232
|
+
|
|
233
|
+
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
|
|
234
|
+
WORKDIR /app
|
|
235
|
+
COPY --from=build /app/publish .
|
|
236
|
+
RUN useradd -m appuser && chown -R appuser:appuser /app
|
|
237
|
+
USER appuser
|
|
238
|
+
EXPOSE 8080
|
|
239
|
+
ENTRYPOINT ["dotnet", "App.dll"]
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Health Checks
|
|
243
|
+
|
|
244
|
+
Always implement health endpoints:
|
|
245
|
+
|
|
246
|
+
```dockerfile
|
|
247
|
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
|
248
|
+
CMD curl -f http://localhost:3000/health || exit 1
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Environment Variables
|
|
252
|
+
|
|
253
|
+
```dockerfile
|
|
254
|
+
# Build-time args
|
|
255
|
+
ARG NODE_ENV=production
|
|
256
|
+
ARG APP_VERSION
|
|
257
|
+
|
|
258
|
+
# Runtime environment
|
|
259
|
+
ENV NODE_ENV=${NODE_ENV} \
|
|
260
|
+
APP_VERSION=${APP_VERSION}
|
|
261
|
+
|
|
262
|
+
# Never put secrets in Dockerfile!
|
|
263
|
+
# Use: docker run -e SECRET=xxx or docker-compose with env_file
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Anti-patterns
|
|
267
|
+
|
|
268
|
+
- Using `:latest` tag
|
|
269
|
+
- Running as root
|
|
270
|
+
- Copying entire context before installing deps
|
|
271
|
+
- Not using multi-stage builds
|
|
272
|
+
- Hardcoding secrets in Dockerfile
|
|
273
|
+
- Not setting resource limits
|
|
274
|
+
- Missing health checks
|
|
275
|
+
- Not using .dockerignore
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "libs/**/*"
|
|
4
|
+
- "apps/**/*"
|
|
5
|
+
- "nx.json"
|
|
6
|
+
- "project.json"
|
|
7
|
+
- "*.ts"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Nx Monorepo Rules
|
|
11
|
+
|
|
12
|
+
## Project Structure
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
workspace/
|
|
16
|
+
├── apps/ # Deployable applications
|
|
17
|
+
│ ├── web/ # Main app
|
|
18
|
+
│ └── web-e2e/ # E2E tests
|
|
19
|
+
├── libs/ # Reusable libraries
|
|
20
|
+
│ ├── shared/ # Cross-domain (ui, util)
|
|
21
|
+
│ └── [domain]/ # Domain-specific
|
|
22
|
+
│ ├── feature-*/ # Smart components, pages
|
|
23
|
+
│ ├── ui-*/ # Dumb/presentational components
|
|
24
|
+
│ ├── data-access-*/ # State, API services
|
|
25
|
+
│ └── util-*/ # Pure functions, helpers
|
|
26
|
+
├── nx.json
|
|
27
|
+
└── tsconfig.base.json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Library Types
|
|
31
|
+
|
|
32
|
+
| Type | Purpose | Can Import |
|
|
33
|
+
|------|---------|------------|
|
|
34
|
+
| `feature` | Smart components, routing, pages | ui, data-access, util |
|
|
35
|
+
| `ui` | Presentational components | util only |
|
|
36
|
+
| `data-access` | State management, API calls | util only |
|
|
37
|
+
| `util` | Pure functions, types, constants | util only |
|
|
38
|
+
|
|
39
|
+
## Naming Convention
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
libs/[scope]/[type]-[name]
|
|
43
|
+
|
|
44
|
+
# Examples
|
|
45
|
+
libs/users/feature-list # User list page
|
|
46
|
+
libs/users/ui-avatar # Avatar component
|
|
47
|
+
libs/users/data-access # User state/API
|
|
48
|
+
libs/users/util-permissions # Permission helpers
|
|
49
|
+
libs/shared/ui-button # Shared button
|
|
50
|
+
libs/shared/util-format # Format utilities
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Tags & Boundaries
|
|
54
|
+
|
|
55
|
+
### nx.json
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"targetDefaults": {
|
|
60
|
+
"build": { "dependsOn": ["^build"] },
|
|
61
|
+
"test": { "dependsOn": ["build"] }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### project.json tags
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"tags": ["scope:users", "type:feature"]
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### .eslintrc.json boundaries
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"rules": {
|
|
79
|
+
"@nx/enforce-module-boundaries": [
|
|
80
|
+
"error",
|
|
81
|
+
{
|
|
82
|
+
"depConstraints": [
|
|
83
|
+
{ "sourceTag": "type:feature", "onlyDependOnLibsWithTags": ["type:ui", "type:data-access", "type:util"] },
|
|
84
|
+
{ "sourceTag": "type:ui", "onlyDependOnLibsWithTags": ["type:util"] },
|
|
85
|
+
{ "sourceTag": "type:data-access", "onlyDependOnLibsWithTags": ["type:util"] },
|
|
86
|
+
{ "sourceTag": "type:util", "onlyDependOnLibsWithTags": ["type:util"] },
|
|
87
|
+
{ "sourceTag": "scope:shared", "onlyDependOnLibsWithTags": ["scope:shared"] },
|
|
88
|
+
{ "sourceTag": "scope:users", "onlyDependOnLibsWithTags": ["scope:users", "scope:shared"] }
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Dependency Flow
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
┌─────────────────────────────────────────────┐
|
|
100
|
+
│ apps/ │
|
|
101
|
+
│ (can import any lib) │
|
|
102
|
+
└─────────────────────────────────────────────┘
|
|
103
|
+
│
|
|
104
|
+
▼
|
|
105
|
+
┌─────────────────────────────────────────────┐
|
|
106
|
+
│ feature libs │
|
|
107
|
+
│ (pages, smart components, routing) │
|
|
108
|
+
└─────────────────────────────────────────────┘
|
|
109
|
+
│ │
|
|
110
|
+
▼ ▼
|
|
111
|
+
┌──────────────────┐ ┌──────────────────┐
|
|
112
|
+
│ ui libs │ │ data-access │
|
|
113
|
+
│ (presentational)│ │ (state, API) │
|
|
114
|
+
└──────────────────┘ └──────────────────┘
|
|
115
|
+
│ │
|
|
116
|
+
└───────┬────────┘
|
|
117
|
+
▼
|
|
118
|
+
┌─────────────────────────────────────────────┐
|
|
119
|
+
│ util libs │
|
|
120
|
+
│ (pure functions, types, constants) │
|
|
121
|
+
└─────────────────────────────────────────────┘
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Commands Reference
|
|
125
|
+
|
|
126
|
+
| Command | Description |
|
|
127
|
+
|---------|-------------|
|
|
128
|
+
| `nx affected -t test` | Test affected projects |
|
|
129
|
+
| `nx affected -t build` | Build affected projects |
|
|
130
|
+
| `nx affected -t lint` | Lint affected projects |
|
|
131
|
+
| `nx run-many -t test --all` | Test all projects |
|
|
132
|
+
| `nx graph` | Visualize dependency graph |
|
|
133
|
+
| `nx reset` | Clear cache |
|
|
134
|
+
|
|
135
|
+
## Generator Commands
|
|
136
|
+
|
|
137
|
+
| Command | Description |
|
|
138
|
+
|---------|-------------|
|
|
139
|
+
| `nx g @nx/angular:lib [name]` | Generate Angular library |
|
|
140
|
+
| `nx g @nx/angular:component [name]` | Generate component |
|
|
141
|
+
| `nx g @nx/angular:service [name]` | Generate service |
|
|
142
|
+
| `nx g remove [name]` | Remove project |
|
|
143
|
+
| `nx g move --project [name] [dest]` | Move project |
|
|
144
|
+
|
|
145
|
+
## Import Paths
|
|
146
|
+
|
|
147
|
+
Always use workspace paths, never relative imports across libs:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// GOOD - workspace path
|
|
151
|
+
import { UserService } from '@myorg/users/data-access';
|
|
152
|
+
import { ButtonComponent } from '@myorg/shared/ui-button';
|
|
153
|
+
|
|
154
|
+
// BAD - relative cross-lib import
|
|
155
|
+
import { UserService } from '../../../users/data-access/src';
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Anti-patterns
|
|
159
|
+
|
|
160
|
+
- Never import from `apps/` into `libs/`
|
|
161
|
+
- Never create circular dependencies between libs
|
|
162
|
+
- Never import `feature` into `ui` or `data-access`
|
|
163
|
+
- Never import domain-specific into `shared/` scope
|
|
164
|
+
- Never skip the public API (`index.ts`)
|
|
165
|
+
|
|
166
|
+
## Caching
|
|
167
|
+
|
|
168
|
+
Nx caches build/test results. To leverage:
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
// nx.json
|
|
172
|
+
{
|
|
173
|
+
"tasksRunnerOptions": {
|
|
174
|
+
"default": {
|
|
175
|
+
"runner": "nx/tasks-runners/default",
|
|
176
|
+
"options": {
|
|
177
|
+
"cacheableOperations": ["build", "lint", "test", "e2e"]
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Affected Commands
|
|
185
|
+
|
|
186
|
+
Always use `affected` in CI:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# Only test what changed
|
|
190
|
+
nx affected -t test --base=main --head=HEAD
|
|
191
|
+
|
|
192
|
+
# Only build what changed
|
|
193
|
+
nx affected -t build --base=main --head=HEAD
|
|
194
|
+
```
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/controllers/**"
|
|
4
|
+
- "**/routes/**"
|
|
5
|
+
- "**/routers/**"
|
|
6
|
+
- "**/endpoints/**"
|
|
7
|
+
- "**/*.controller.ts"
|
|
8
|
+
- "**/*_router.py"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# API Design Principles
|
|
12
|
+
|
|
13
|
+
## REST Conventions
|
|
14
|
+
|
|
15
|
+
### HTTP Methods
|
|
16
|
+
|
|
17
|
+
| Method | Usage | Idempotent | Response |
|
|
18
|
+
|--------|-------|------------|----------|
|
|
19
|
+
| `GET` | Read resource(s) | Yes | 200 + data |
|
|
20
|
+
| `POST` | Create resource | No | 201 + created resource |
|
|
21
|
+
| `PUT` | Full update | Yes | 200 + updated resource |
|
|
22
|
+
| `PATCH` | Partial update | Yes | 200 + updated resource |
|
|
23
|
+
| `DELETE` | Remove resource | Yes | 204 No Content |
|
|
24
|
+
|
|
25
|
+
### URL Structure
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
GET /api/v1/users # List users
|
|
29
|
+
GET /api/v1/users/:id # Get single user
|
|
30
|
+
POST /api/v1/users # Create user
|
|
31
|
+
PUT /api/v1/users/:id # Replace user
|
|
32
|
+
PATCH /api/v1/users/:id # Update user fields
|
|
33
|
+
DELETE /api/v1/users/:id # Delete user
|
|
34
|
+
|
|
35
|
+
# Nested resources (max 2 levels)
|
|
36
|
+
GET /api/v1/users/:id/orders
|
|
37
|
+
POST /api/v1/users/:id/orders
|
|
38
|
+
|
|
39
|
+
# Actions (when CRUD doesn't fit)
|
|
40
|
+
POST /api/v1/users/:id/activate
|
|
41
|
+
POST /api/v1/orders/:id/cancel
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Naming Rules
|
|
45
|
+
|
|
46
|
+
- Use **plural nouns**: `/users`, `/orders`, `/products`
|
|
47
|
+
- Use **kebab-case**: `/user-profiles`, `/order-items`
|
|
48
|
+
- Avoid verbs in URLs (use HTTP methods instead)
|
|
49
|
+
- Use query params for filtering: `/users?status=active&role=admin`
|
|
50
|
+
|
|
51
|
+
## Response Format
|
|
52
|
+
|
|
53
|
+
### Success Response
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"data": { ... },
|
|
58
|
+
"meta": {
|
|
59
|
+
"timestamp": "2024-01-15T10:30:00Z",
|
|
60
|
+
"requestId": "abc-123"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### List Response with Pagination
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"data": [ ... ],
|
|
70
|
+
"meta": {
|
|
71
|
+
"total": 100,
|
|
72
|
+
"page": 1,
|
|
73
|
+
"pageSize": 20,
|
|
74
|
+
"totalPages": 5,
|
|
75
|
+
"hasNext": true,
|
|
76
|
+
"hasPrevious": false
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Error Response (RFC 7807 Problem Details)
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"type": "https://api.example.com/errors/validation",
|
|
86
|
+
"title": "Validation Error",
|
|
87
|
+
"status": 400,
|
|
88
|
+
"detail": "One or more fields failed validation",
|
|
89
|
+
"instance": "/api/v1/users",
|
|
90
|
+
"errors": [
|
|
91
|
+
{ "field": "email", "message": "Invalid email format" },
|
|
92
|
+
{ "field": "age", "message": "Must be at least 18" }
|
|
93
|
+
],
|
|
94
|
+
"traceId": "abc-123"
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Status Codes
|
|
99
|
+
|
|
100
|
+
| Code | When to Use |
|
|
101
|
+
|------|-------------|
|
|
102
|
+
| `200` | Successful GET, PUT, PATCH |
|
|
103
|
+
| `201` | Successful POST (resource created) |
|
|
104
|
+
| `204` | Successful DELETE (no content) |
|
|
105
|
+
| `400` | Bad request (validation error) |
|
|
106
|
+
| `401` | Unauthorized (not authenticated) |
|
|
107
|
+
| `403` | Forbidden (authenticated but not allowed) |
|
|
108
|
+
| `404` | Resource not found |
|
|
109
|
+
| `409` | Conflict (duplicate resource) |
|
|
110
|
+
| `422` | Unprocessable entity (business rule violation) |
|
|
111
|
+
| `429` | Too many requests (rate limited) |
|
|
112
|
+
| `500` | Internal server error |
|
|
113
|
+
|
|
114
|
+
## Pagination
|
|
115
|
+
|
|
116
|
+
### Offset-based (simple, for small datasets)
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
GET /api/v1/users?page=2&pageSize=20
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Cursor-based (performant, for large datasets)
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
GET /api/v1/users?cursor=eyJpZCI6MTAwfQ&limit=20
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Response includes next cursor:
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"data": [...],
|
|
132
|
+
"meta": {
|
|
133
|
+
"nextCursor": "eyJpZCI6MTIwfQ",
|
|
134
|
+
"hasMore": true
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Filtering & Sorting
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
# Filtering
|
|
143
|
+
GET /api/v1/users?status=active&role=admin
|
|
144
|
+
GET /api/v1/orders?createdAt[gte]=2024-01-01&createdAt[lte]=2024-12-31
|
|
145
|
+
|
|
146
|
+
# Sorting
|
|
147
|
+
GET /api/v1/users?sort=createdAt:desc
|
|
148
|
+
GET /api/v1/users?sort=lastName:asc,firstName:asc
|
|
149
|
+
|
|
150
|
+
# Field selection (sparse fieldsets)
|
|
151
|
+
GET /api/v1/users?fields=id,name,email
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Versioning
|
|
155
|
+
|
|
156
|
+
### URL Path (recommended)
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
/api/v1/users
|
|
160
|
+
/api/v2/users
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Header-based (alternative)
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
Accept: application/vnd.api+json; version=1
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Rate Limiting
|
|
170
|
+
|
|
171
|
+
Include headers in response:
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
X-RateLimit-Limit: 100
|
|
175
|
+
X-RateLimit-Remaining: 95
|
|
176
|
+
X-RateLimit-Reset: 1640000000
|
|
177
|
+
Retry-After: 60 (when 429)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## HATEOAS (optional, for discoverability)
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"data": {
|
|
185
|
+
"id": "123",
|
|
186
|
+
"name": "John"
|
|
187
|
+
},
|
|
188
|
+
"links": {
|
|
189
|
+
"self": "/api/v1/users/123",
|
|
190
|
+
"orders": "/api/v1/users/123/orders",
|
|
191
|
+
"profile": "/api/v1/users/123/profile"
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Anti-patterns
|
|
197
|
+
|
|
198
|
+
- Verbs in URLs: `/api/getUsers` → `/api/users`
|
|
199
|
+
- Singular nouns: `/api/user/123` → `/api/users/123`
|
|
200
|
+
- Deeply nested: `/api/users/1/orders/2/items/3` → `/api/order-items/3`
|
|
201
|
+
- Inconsistent casing: `/api/userProfiles` → `/api/user-profiles`
|
|
202
|
+
- Returning 200 for errors
|
|
203
|
+
- Exposing internal IDs or sensitive data
|