@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.
Files changed (133) hide show
  1. package/README.md +270 -121
  2. package/bin/cli.js +5 -2
  3. package/configs/_shared/.claude/rules/conventions/documentation.md +324 -0
  4. package/configs/_shared/.claude/rules/conventions/git.md +265 -0
  5. package/configs/_shared/.claude/rules/{performance.md → conventions/performance.md} +1 -1
  6. package/configs/_shared/.claude/rules/conventions/principles.md +334 -0
  7. package/configs/_shared/.claude/rules/devops/ci-cd.md +262 -0
  8. package/configs/_shared/.claude/rules/devops/docker.md +275 -0
  9. package/configs/_shared/.claude/rules/devops/nx.md +194 -0
  10. package/configs/_shared/.claude/rules/domain/backend/api-design.md +203 -0
  11. package/configs/_shared/.claude/rules/lang/csharp/async.md +220 -0
  12. package/configs/_shared/.claude/rules/lang/csharp/csharp.md +314 -0
  13. package/configs/_shared/.claude/rules/lang/csharp/linq.md +210 -0
  14. package/configs/_shared/.claude/rules/lang/python/async.md +337 -0
  15. package/configs/_shared/.claude/rules/lang/python/celery.md +476 -0
  16. package/configs/_shared/.claude/rules/lang/python/config.md +339 -0
  17. package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/database/sqlalchemy.md +6 -1
  18. package/configs/_shared/.claude/rules/lang/python/deployment.md +523 -0
  19. package/configs/_shared/.claude/rules/lang/python/error-handling.md +330 -0
  20. package/configs/_shared/.claude/rules/lang/python/migrations.md +421 -0
  21. package/configs/_shared/.claude/rules/lang/python/python.md +172 -0
  22. package/configs/_shared/.claude/rules/lang/python/repository.md +383 -0
  23. package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/testing.md +2 -69
  24. package/configs/_shared/.claude/rules/lang/typescript/async.md +447 -0
  25. package/configs/_shared/.claude/rules/lang/typescript/generics.md +356 -0
  26. package/configs/_shared/.claude/rules/lang/typescript/typescript.md +212 -0
  27. package/configs/_shared/.claude/rules/quality/error-handling.md +48 -0
  28. package/configs/_shared/.claude/rules/quality/logging.md +45 -0
  29. package/configs/_shared/.claude/rules/quality/observability.md +240 -0
  30. package/configs/_shared/.claude/rules/quality/testing-patterns.md +65 -0
  31. package/configs/_shared/.claude/rules/security/secrets-management.md +222 -0
  32. package/configs/_shared/.claude/skills/analysis/explore/SKILL.md +257 -0
  33. package/configs/_shared/.claude/skills/analysis/security-audit/SKILL.md +184 -0
  34. package/configs/_shared/.claude/skills/dev/api-endpoint/SKILL.md +126 -0
  35. package/configs/_shared/.claude/{commands/generate-tests.md → skills/dev/generate-tests/SKILL.md} +6 -0
  36. package/configs/_shared/.claude/{commands/fix-issue.md → skills/git/fix-issue/SKILL.md} +6 -0
  37. package/configs/_shared/.claude/{commands/review-pr.md → skills/git/review-pr/SKILL.md} +6 -0
  38. package/configs/_shared/.claude/skills/infra/deploy/SKILL.md +139 -0
  39. package/configs/_shared/.claude/skills/infra/docker/SKILL.md +95 -0
  40. package/configs/_shared/.claude/skills/infra/migration/SKILL.md +158 -0
  41. package/configs/_shared/.claude/skills/nx/nx-affected/SKILL.md +72 -0
  42. package/configs/_shared/.claude/skills/nx/nx-lib/SKILL.md +375 -0
  43. package/configs/_shared/CLAUDE.md +52 -149
  44. package/configs/angular/.claude/rules/{components.md → core/components.md} +69 -15
  45. package/configs/angular/.claude/rules/core/resource.md +285 -0
  46. package/configs/angular/.claude/rules/core/signals.md +323 -0
  47. package/configs/angular/.claude/rules/http.md +338 -0
  48. package/configs/angular/.claude/rules/routing.md +291 -0
  49. package/configs/angular/.claude/rules/ssr.md +312 -0
  50. package/configs/angular/.claude/rules/state/signal-store.md +408 -0
  51. package/configs/angular/.claude/rules/{state.md → state/state.md} +2 -2
  52. package/configs/angular/.claude/rules/testing.md +7 -7
  53. package/configs/angular/.claude/rules/ui/aria.md +422 -0
  54. package/configs/angular/.claude/rules/ui/forms.md +424 -0
  55. package/configs/angular/.claude/rules/ui/pipes-directives.md +335 -0
  56. package/configs/angular/.claude/settings.json +1 -0
  57. package/configs/angular/.claude/skills/ngrx-slice/SKILL.md +362 -0
  58. package/configs/angular/.claude/skills/signal-store/SKILL.md +445 -0
  59. package/configs/angular/CLAUDE.md +24 -216
  60. package/configs/dotnet/.claude/rules/background-services.md +552 -0
  61. package/configs/dotnet/.claude/rules/configuration.md +426 -0
  62. package/configs/dotnet/.claude/rules/ddd.md +447 -0
  63. package/configs/dotnet/.claude/rules/dependency-injection.md +343 -0
  64. package/configs/dotnet/.claude/rules/mediatr.md +320 -0
  65. package/configs/dotnet/.claude/rules/middleware.md +489 -0
  66. package/configs/dotnet/.claude/rules/result-pattern.md +363 -0
  67. package/configs/dotnet/.claude/rules/validation.md +388 -0
  68. package/configs/dotnet/.claude/settings.json +21 -3
  69. package/configs/dotnet/CLAUDE.md +53 -286
  70. package/configs/fastapi/.claude/rules/background-tasks.md +254 -0
  71. package/configs/fastapi/.claude/rules/dependencies.md +170 -0
  72. package/configs/{python → fastapi}/.claude/rules/fastapi.md +61 -1
  73. package/configs/fastapi/.claude/rules/lifespan.md +274 -0
  74. package/configs/fastapi/.claude/rules/middleware.md +229 -0
  75. package/configs/fastapi/.claude/rules/pydantic.md +433 -0
  76. package/configs/fastapi/.claude/rules/responses.md +251 -0
  77. package/configs/fastapi/.claude/rules/routers.md +202 -0
  78. package/configs/fastapi/.claude/rules/security.md +222 -0
  79. package/configs/fastapi/.claude/rules/testing.md +251 -0
  80. package/configs/fastapi/.claude/rules/websockets.md +298 -0
  81. package/configs/fastapi/.claude/settings.json +33 -0
  82. package/configs/fastapi/CLAUDE.md +144 -0
  83. package/configs/flask/.claude/rules/blueprints.md +208 -0
  84. package/configs/flask/.claude/rules/cli.md +285 -0
  85. package/configs/flask/.claude/rules/configuration.md +281 -0
  86. package/configs/flask/.claude/rules/context.md +238 -0
  87. package/configs/flask/.claude/rules/error-handlers.md +278 -0
  88. package/configs/flask/.claude/rules/extensions.md +278 -0
  89. package/configs/flask/.claude/rules/flask.md +171 -0
  90. package/configs/flask/.claude/rules/marshmallow.md +206 -0
  91. package/configs/flask/.claude/rules/security.md +267 -0
  92. package/configs/flask/.claude/rules/testing.md +284 -0
  93. package/configs/flask/.claude/settings.json +33 -0
  94. package/configs/flask/CLAUDE.md +166 -0
  95. package/configs/nestjs/.claude/rules/common-patterns.md +300 -0
  96. package/configs/nestjs/.claude/rules/filters.md +376 -0
  97. package/configs/nestjs/.claude/rules/interceptors.md +317 -0
  98. package/configs/nestjs/.claude/rules/middleware.md +321 -0
  99. package/configs/nestjs/.claude/rules/modules.md +26 -0
  100. package/configs/nestjs/.claude/rules/pipes.md +351 -0
  101. package/configs/nestjs/.claude/rules/websockets.md +451 -0
  102. package/configs/nestjs/.claude/settings.json +16 -2
  103. package/configs/nestjs/CLAUDE.md +57 -215
  104. package/configs/nextjs/.claude/rules/api-routes.md +358 -0
  105. package/configs/nextjs/.claude/rules/authentication.md +355 -0
  106. package/configs/nextjs/.claude/rules/components.md +52 -0
  107. package/configs/nextjs/.claude/rules/data-fetching.md +249 -0
  108. package/configs/nextjs/.claude/rules/database.md +400 -0
  109. package/configs/nextjs/.claude/rules/middleware.md +303 -0
  110. package/configs/nextjs/.claude/rules/routing.md +324 -0
  111. package/configs/nextjs/.claude/rules/seo.md +350 -0
  112. package/configs/nextjs/.claude/rules/server-actions.md +353 -0
  113. package/configs/nextjs/.claude/rules/state/zustand.md +6 -6
  114. package/configs/nextjs/.claude/settings.json +5 -0
  115. package/configs/nextjs/CLAUDE.md +69 -331
  116. package/package.json +23 -9
  117. package/src/cli.js +220 -0
  118. package/src/config.js +29 -0
  119. package/src/index.js +13 -0
  120. package/src/installer.js +361 -0
  121. package/src/merge.js +116 -0
  122. package/src/tech-config.json +29 -0
  123. package/src/utils.js +96 -0
  124. package/configs/python/.claude/rules/flask.md +0 -332
  125. package/configs/python/.claude/settings.json +0 -18
  126. package/configs/python/CLAUDE.md +0 -273
  127. package/src/install.js +0 -315
  128. /package/configs/_shared/.claude/rules/{accessibility.md → domain/frontend/accessibility.md} +0 -0
  129. /package/configs/_shared/.claude/rules/{security.md → security/security.md} +0 -0
  130. /package/configs/_shared/.claude/skills/{debug → dev/debug}/SKILL.md +0 -0
  131. /package/configs/_shared/.claude/skills/{learning → dev/learning}/SKILL.md +0 -0
  132. /package/configs/_shared/.claude/skills/{spec → dev/spec}/SKILL.md +0 -0
  133. /package/configs/_shared/.claude/skills/{review → git/review}/SKILL.md +0 -0
@@ -0,0 +1,451 @@
1
+ ---
2
+ paths:
3
+ - "**/*.gateway.ts"
4
+ - "**/gateways/**/*.ts"
5
+ - "**/websocket/**/*.ts"
6
+ ---
7
+
8
+ # NestJS WebSockets
9
+
10
+ ## Gateway Setup
11
+
12
+ ```typescript
13
+ // gateways/events.gateway.ts
14
+ import {
15
+ WebSocketGateway,
16
+ WebSocketServer,
17
+ SubscribeMessage,
18
+ MessageBody,
19
+ ConnectedSocket,
20
+ OnGatewayConnection,
21
+ OnGatewayDisconnect,
22
+ OnGatewayInit,
23
+ } from '@nestjs/websockets';
24
+ import { Server, Socket } from 'socket.io';
25
+ import { Logger } from '@nestjs/common';
26
+
27
+ @WebSocketGateway({
28
+ cors: {
29
+ origin: process.env.ALLOWED_ORIGINS?.split(',') || '*',
30
+ credentials: true,
31
+ },
32
+ namespace: '/events',
33
+ })
34
+ export class EventsGateway
35
+ implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
36
+ {
37
+ private readonly logger = new Logger(EventsGateway.name);
38
+
39
+ @WebSocketServer()
40
+ server: Server;
41
+
42
+ afterInit(server: Server) {
43
+ this.logger.log('WebSocket Gateway initialized');
44
+ }
45
+
46
+ handleConnection(client: Socket) {
47
+ this.logger.log(`Client connected: ${client.id}`);
48
+ }
49
+
50
+ handleDisconnect(client: Socket) {
51
+ this.logger.log(`Client disconnected: ${client.id}`);
52
+ }
53
+
54
+ @SubscribeMessage('message')
55
+ handleMessage(
56
+ @MessageBody() data: { text: string },
57
+ @ConnectedSocket() client: Socket,
58
+ ) {
59
+ this.logger.log(`Message from ${client.id}: ${data.text}`);
60
+ return { event: 'message', data: { received: true } };
61
+ }
62
+ }
63
+ ```
64
+
65
+ ## Authentication
66
+
67
+ ```typescript
68
+ // gateways/auth.gateway.ts
69
+ import { WebSocketGateway, OnGatewayConnection } from '@nestjs/websockets';
70
+ import { Socket } from 'socket.io';
71
+ import { JwtService } from '@nestjs/jwt';
72
+ import { UnauthorizedException } from '@nestjs/common';
73
+
74
+ @WebSocketGateway()
75
+ export class AuthenticatedGateway implements OnGatewayConnection {
76
+ constructor(private readonly jwtService: JwtService) {}
77
+
78
+ async handleConnection(client: Socket) {
79
+ try {
80
+ const token = this.extractToken(client);
81
+
82
+ if (!token) {
83
+ throw new UnauthorizedException('No token provided');
84
+ }
85
+
86
+ const payload = await this.jwtService.verifyAsync(token);
87
+ client.data.user = payload;
88
+
89
+ // Join user-specific room
90
+ client.join(`user:${payload.sub}`);
91
+ } catch (error) {
92
+ client.emit('error', { message: 'Authentication failed' });
93
+ client.disconnect();
94
+ }
95
+ }
96
+
97
+ private extractToken(client: Socket): string | null {
98
+ // From auth header
99
+ const authHeader = client.handshake.headers.authorization;
100
+ if (authHeader?.startsWith('Bearer ')) {
101
+ return authHeader.split(' ')[1];
102
+ }
103
+
104
+ // From query param
105
+ return client.handshake.query.token as string | null;
106
+ }
107
+ }
108
+ ```
109
+
110
+ ## Rooms and Broadcasting
111
+
112
+ ```typescript
113
+ // gateways/chat.gateway.ts
114
+ import {
115
+ WebSocketGateway,
116
+ WebSocketServer,
117
+ SubscribeMessage,
118
+ MessageBody,
119
+ ConnectedSocket,
120
+ } from '@nestjs/websockets';
121
+ import { Server, Socket } from 'socket.io';
122
+
123
+ @WebSocketGateway({ namespace: '/chat' })
124
+ export class ChatGateway {
125
+ @WebSocketServer()
126
+ server: Server;
127
+
128
+ @SubscribeMessage('joinRoom')
129
+ handleJoinRoom(
130
+ @MessageBody() roomId: string,
131
+ @ConnectedSocket() client: Socket,
132
+ ) {
133
+ client.join(`room:${roomId}`);
134
+
135
+ // Notify room
136
+ this.server.to(`room:${roomId}`).emit('userJoined', {
137
+ userId: client.data.user.sub,
138
+ roomId,
139
+ });
140
+
141
+ return { joined: roomId };
142
+ }
143
+
144
+ @SubscribeMessage('leaveRoom')
145
+ handleLeaveRoom(
146
+ @MessageBody() roomId: string,
147
+ @ConnectedSocket() client: Socket,
148
+ ) {
149
+ client.leave(`room:${roomId}`);
150
+
151
+ this.server.to(`room:${roomId}`).emit('userLeft', {
152
+ userId: client.data.user.sub,
153
+ roomId,
154
+ });
155
+
156
+ return { left: roomId };
157
+ }
158
+
159
+ @SubscribeMessage('sendMessage')
160
+ handleSendMessage(
161
+ @MessageBody() data: { roomId: string; message: string },
162
+ @ConnectedSocket() client: Socket,
163
+ ) {
164
+ const payload = {
165
+ userId: client.data.user.sub,
166
+ message: data.message,
167
+ timestamp: new Date().toISOString(),
168
+ };
169
+
170
+ // Broadcast to room except sender
171
+ client.to(`room:${data.roomId}`).emit('newMessage', payload);
172
+
173
+ return { sent: true };
174
+ }
175
+
176
+ // Broadcast from service
177
+ broadcastToRoom(roomId: string, event: string, data: unknown) {
178
+ this.server.to(`room:${roomId}`).emit(event, data);
179
+ }
180
+
181
+ // Send to specific user
182
+ sendToUser(userId: string, event: string, data: unknown) {
183
+ this.server.to(`user:${userId}`).emit(event, data);
184
+ }
185
+ }
186
+ ```
187
+
188
+ ## Validation with DTOs
189
+
190
+ ```typescript
191
+ // dto/message.dto.ts
192
+ import { IsString, IsNotEmpty, MaxLength, IsUUID } from 'class-validator';
193
+
194
+ export class SendMessageDto {
195
+ @IsUUID()
196
+ roomId: string;
197
+
198
+ @IsString()
199
+ @IsNotEmpty()
200
+ @MaxLength(1000)
201
+ message: string;
202
+ }
203
+
204
+ // gateways/validated.gateway.ts
205
+ import { UsePipes, ValidationPipe } from '@nestjs/common';
206
+
207
+ @WebSocketGateway()
208
+ @UsePipes(new ValidationPipe({ transform: true }))
209
+ export class ValidatedGateway {
210
+ @SubscribeMessage('sendMessage')
211
+ handleMessage(@MessageBody() data: SendMessageDto) {
212
+ // data is validated
213
+ return { success: true };
214
+ }
215
+ }
216
+ ```
217
+
218
+ ## Guards for WebSockets
219
+
220
+ ```typescript
221
+ // guards/ws-auth.guard.ts
222
+ import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
223
+ import { WsException } from '@nestjs/websockets';
224
+ import { Socket } from 'socket.io';
225
+
226
+ @Injectable()
227
+ export class WsAuthGuard implements CanActivate {
228
+ canActivate(context: ExecutionContext): boolean {
229
+ const client = context.switchToWs().getClient<Socket>();
230
+
231
+ if (!client.data.user) {
232
+ throw new WsException('Unauthorized');
233
+ }
234
+
235
+ return true;
236
+ }
237
+ }
238
+
239
+ // guards/ws-role.guard.ts
240
+ @Injectable()
241
+ export class WsRoleGuard implements CanActivate {
242
+ constructor(private readonly requiredRole: string) {}
243
+
244
+ canActivate(context: ExecutionContext): boolean {
245
+ const client = context.switchToWs().getClient<Socket>();
246
+ const user = client.data.user;
247
+
248
+ if (user?.role !== this.requiredRole) {
249
+ throw new WsException('Forbidden');
250
+ }
251
+
252
+ return true;
253
+ }
254
+ }
255
+
256
+ // Usage
257
+ @WebSocketGateway()
258
+ @UseGuards(WsAuthGuard)
259
+ export class ProtectedGateway {
260
+ @SubscribeMessage('adminAction')
261
+ @UseGuards(new WsRoleGuard('admin'))
262
+ handleAdminAction() {
263
+ return { success: true };
264
+ }
265
+ }
266
+ ```
267
+
268
+ ## Exception Handling
269
+
270
+ ```typescript
271
+ // filters/ws-exception.filter.ts
272
+ import { Catch, ArgumentsHost } from '@nestjs/common';
273
+ import { BaseWsExceptionFilter, WsException } from '@nestjs/websockets';
274
+ import { Socket } from 'socket.io';
275
+
276
+ @Catch()
277
+ export class AllWsExceptionsFilter extends BaseWsExceptionFilter {
278
+ catch(exception: unknown, host: ArgumentsHost) {
279
+ const client = host.switchToWs().getClient<Socket>();
280
+
281
+ const error =
282
+ exception instanceof WsException
283
+ ? exception.getError()
284
+ : { message: 'Internal error', code: 'INTERNAL_ERROR' };
285
+
286
+ client.emit('exception', {
287
+ status: 'error',
288
+ ...error,
289
+ });
290
+ }
291
+ }
292
+
293
+ // Apply globally in gateway
294
+ @WebSocketGateway()
295
+ @UseFilters(AllWsExceptionsFilter)
296
+ export class EventsGateway {}
297
+ ```
298
+
299
+ ## Interceptors
300
+
301
+ ```typescript
302
+ // interceptors/ws-logging.interceptor.ts
303
+ import {
304
+ Injectable,
305
+ NestInterceptor,
306
+ ExecutionContext,
307
+ CallHandler,
308
+ Logger,
309
+ } from '@nestjs/common';
310
+ import { Observable } from 'rxjs';
311
+ import { tap } from 'rxjs/operators';
312
+
313
+ @Injectable()
314
+ export class WsLoggingInterceptor implements NestInterceptor {
315
+ private readonly logger = new Logger(WsLoggingInterceptor.name);
316
+
317
+ intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
318
+ const client = context.switchToWs().getClient();
319
+ const data = context.switchToWs().getData();
320
+ const pattern = context.switchToWs().getPattern();
321
+
322
+ const start = Date.now();
323
+
324
+ return next.handle().pipe(
325
+ tap(() => {
326
+ const duration = Date.now() - start;
327
+ this.logger.log(
328
+ `WS ${pattern} - Client: ${client.id} - ${duration}ms`,
329
+ );
330
+ }),
331
+ );
332
+ }
333
+ }
334
+ ```
335
+
336
+ ## Redis Adapter (Scaling)
337
+
338
+ ```typescript
339
+ // main.ts
340
+ import { IoAdapter } from '@nestjs/platform-socket.io';
341
+ import { createAdapter } from '@socket.io/redis-adapter';
342
+ import { createClient } from 'redis';
343
+
344
+ async function bootstrap() {
345
+ const app = await NestFactory.create(AppModule);
346
+
347
+ const pubClient = createClient({ url: process.env.REDIS_URL });
348
+ const subClient = pubClient.duplicate();
349
+
350
+ await Promise.all([pubClient.connect(), subClient.connect()]);
351
+
352
+ const redisAdapter = createAdapter(pubClient, subClient);
353
+
354
+ app.useWebSocketAdapter(new IoAdapter(app).createIOServer(3001, {
355
+ adapter: redisAdapter,
356
+ }));
357
+
358
+ await app.listen(3000);
359
+ }
360
+ ```
361
+
362
+ ## Testing WebSockets
363
+
364
+ ```typescript
365
+ // gateways/events.gateway.spec.ts
366
+ import { Test } from '@nestjs/testing';
367
+ import { INestApplication } from '@nestjs/common';
368
+ import { io, Socket } from 'socket.io-client';
369
+ import { EventsGateway } from './events.gateway';
370
+
371
+ describe('EventsGateway', () => {
372
+ let app: INestApplication;
373
+ let client: Socket;
374
+
375
+ beforeAll(async () => {
376
+ const moduleRef = await Test.createTestingModule({
377
+ providers: [EventsGateway],
378
+ }).compile();
379
+
380
+ app = moduleRef.createNestApplication();
381
+ await app.listen(3001);
382
+ });
383
+
384
+ beforeEach((done) => {
385
+ client = io('http://localhost:3001/events');
386
+ client.on('connect', done);
387
+ });
388
+
389
+ afterEach(() => {
390
+ client.disconnect();
391
+ });
392
+
393
+ afterAll(async () => {
394
+ await app.close();
395
+ });
396
+
397
+ it('should receive message response', (done) => {
398
+ client.emit('message', { text: 'hello' }, (response: unknown) => {
399
+ expect(response).toEqual({ event: 'message', data: { received: true } });
400
+ done();
401
+ });
402
+ });
403
+ });
404
+ ```
405
+
406
+ ## Anti-patterns
407
+
408
+ ```typescript
409
+ // BAD: Not handling disconnections
410
+ @WebSocketGateway()
411
+ export class LeakyGateway {
412
+ private connections = new Map<string, Socket>();
413
+
414
+ handleConnection(client: Socket) {
415
+ this.connections.set(client.id, client);
416
+ // Never cleaned up!
417
+ }
418
+ }
419
+
420
+ // GOOD: Clean up on disconnect
421
+ handleDisconnect(client: Socket) {
422
+ this.connections.delete(client.id);
423
+ }
424
+
425
+ // BAD: Blocking event handlers
426
+ @SubscribeMessage('data')
427
+ async handleData(@MessageBody() data: unknown) {
428
+ await this.heavyOperation(data); // Blocks event loop
429
+ return { done: true };
430
+ }
431
+
432
+ // GOOD: Offload to queue
433
+ @SubscribeMessage('data')
434
+ handleData(@MessageBody() data: unknown) {
435
+ this.queue.add('process', data);
436
+ return { queued: true };
437
+ }
438
+
439
+ // BAD: Not validating input
440
+ @SubscribeMessage('action')
441
+ handleAction(@MessageBody() data: any) {
442
+ return this.service.doSomething(data.id); // Unsafe!
443
+ }
444
+
445
+ // GOOD: Use DTOs with validation
446
+ @UsePipes(ValidationPipe)
447
+ @SubscribeMessage('action')
448
+ handleAction(@MessageBody() data: ActionDto) {
449
+ return this.service.doSomething(data.id);
450
+ }
451
+ ```
@@ -2,14 +2,28 @@
2
2
  "permissions": {
3
3
  "allow": [
4
4
  "Bash(npm run start:dev)",
5
+ "Bash(npm run start:debug)",
5
6
  "Bash(npm run build)",
6
7
  "Bash(npm run test*)",
7
8
  "Bash(npm run lint*)",
8
9
  "Bash(npm run format*)",
10
+ "Bash(npm install *)",
11
+ "Bash(npm ci)",
9
12
  "Bash(npx prisma *)",
10
13
  "Bash(npx typeorm *)",
11
- "Bash(npm install *)"
14
+ "Bash(npx nest *)",
15
+ "Read",
16
+ "Edit",
17
+ "Write"
12
18
  ],
13
- "deny": []
19
+ "deny": [
20
+ "Bash(rm -rf *)",
21
+ "Read(.env)",
22
+ "Read(.env.*)",
23
+ "Read(**/secrets/**)"
24
+ ]
25
+ },
26
+ "env": {
27
+ "NODE_ENV": "development"
14
28
  }
15
29
  }