@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,351 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.pipe.ts"
|
|
4
|
+
- "**/pipes/**/*.ts"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# NestJS Pipes
|
|
8
|
+
|
|
9
|
+
## Built-in Pipes
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import {
|
|
13
|
+
ValidationPipe,
|
|
14
|
+
ParseIntPipe,
|
|
15
|
+
ParseBoolPipe,
|
|
16
|
+
ParseArrayPipe,
|
|
17
|
+
ParseUUIDPipe,
|
|
18
|
+
ParseEnumPipe,
|
|
19
|
+
DefaultValuePipe,
|
|
20
|
+
} from '@nestjs/common';
|
|
21
|
+
|
|
22
|
+
@Controller('users')
|
|
23
|
+
export class UsersController {
|
|
24
|
+
// ParseIntPipe
|
|
25
|
+
@Get(':id')
|
|
26
|
+
findOne(@Param('id', ParseIntPipe) id: number) {
|
|
27
|
+
return this.usersService.findOne(id);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ParseUUIDPipe with version
|
|
31
|
+
@Get(':uuid')
|
|
32
|
+
findByUuid(@Param('uuid', new ParseUUIDPipe({ version: '4' })) uuid: string) {
|
|
33
|
+
return this.usersService.findByUuid(uuid);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ParseEnumPipe
|
|
37
|
+
@Get()
|
|
38
|
+
findByStatus(
|
|
39
|
+
@Query('status', new ParseEnumPipe(UserStatus)) status: UserStatus,
|
|
40
|
+
) {
|
|
41
|
+
return this.usersService.findByStatus(status);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// DefaultValuePipe
|
|
45
|
+
@Get()
|
|
46
|
+
findAll(
|
|
47
|
+
@Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,
|
|
48
|
+
@Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number,
|
|
49
|
+
) {
|
|
50
|
+
return this.usersService.findAll({ page, limit });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ParseArrayPipe
|
|
54
|
+
@Get()
|
|
55
|
+
findByIds(
|
|
56
|
+
@Query('ids', new ParseArrayPipe({ items: Number, separator: ',' }))
|
|
57
|
+
ids: number[],
|
|
58
|
+
) {
|
|
59
|
+
return this.usersService.findByIds(ids);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Global Validation Pipe
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// main.ts
|
|
68
|
+
import { ValidationPipe } from '@nestjs/common';
|
|
69
|
+
|
|
70
|
+
async function bootstrap() {
|
|
71
|
+
const app = await NestFactory.create(AppModule);
|
|
72
|
+
|
|
73
|
+
app.useGlobalPipes(
|
|
74
|
+
new ValidationPipe({
|
|
75
|
+
whitelist: true, // Strip non-decorated properties
|
|
76
|
+
forbidNonWhitelisted: true, // Throw on extra properties
|
|
77
|
+
transform: true, // Transform payloads to DTO types
|
|
78
|
+
transformOptions: {
|
|
79
|
+
enableImplicitConversion: true,
|
|
80
|
+
},
|
|
81
|
+
exceptionFactory: (errors) => {
|
|
82
|
+
const messages = errors.map((error) => ({
|
|
83
|
+
field: error.property,
|
|
84
|
+
constraints: Object.values(error.constraints || {}),
|
|
85
|
+
}));
|
|
86
|
+
return new BadRequestException({ errors: messages });
|
|
87
|
+
},
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
await app.listen(3000);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Custom Pipes
|
|
96
|
+
|
|
97
|
+
### Transform Pipe
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// pipes/parse-date.pipe.ts
|
|
101
|
+
import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';
|
|
102
|
+
|
|
103
|
+
@Injectable()
|
|
104
|
+
export class ParseDatePipe implements PipeTransform<string, Date> {
|
|
105
|
+
transform(value: string): Date {
|
|
106
|
+
const date = new Date(value);
|
|
107
|
+
|
|
108
|
+
if (isNaN(date.getTime())) {
|
|
109
|
+
throw new BadRequestException(`Invalid date format: ${value}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return date;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Usage
|
|
117
|
+
@Get()
|
|
118
|
+
findByDate(@Query('date', ParseDatePipe) date: Date) {
|
|
119
|
+
return this.service.findByDate(date);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Validation Pipe with Schema
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// pipes/zod-validation.pipe.ts
|
|
127
|
+
import { PipeTransform, BadRequestException } from '@nestjs/common';
|
|
128
|
+
import { ZodSchema, ZodError } from 'zod';
|
|
129
|
+
|
|
130
|
+
export class ZodValidationPipe implements PipeTransform {
|
|
131
|
+
constructor(private readonly schema: ZodSchema) {}
|
|
132
|
+
|
|
133
|
+
transform(value: unknown) {
|
|
134
|
+
try {
|
|
135
|
+
return this.schema.parse(value);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (error instanceof ZodError) {
|
|
138
|
+
throw new BadRequestException({
|
|
139
|
+
message: 'Validation failed',
|
|
140
|
+
errors: error.errors.map((e) => ({
|
|
141
|
+
path: e.path.join('.'),
|
|
142
|
+
message: e.message,
|
|
143
|
+
})),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Usage
|
|
152
|
+
const createUserSchema = z.object({
|
|
153
|
+
email: z.string().email(),
|
|
154
|
+
name: z.string().min(2),
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
@Post()
|
|
158
|
+
create(@Body(new ZodValidationPipe(createUserSchema)) dto: CreateUserDto) {
|
|
159
|
+
return this.usersService.create(dto);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Async Pipe
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
// pipes/user-exists.pipe.ts
|
|
167
|
+
import {
|
|
168
|
+
PipeTransform,
|
|
169
|
+
Injectable,
|
|
170
|
+
NotFoundException,
|
|
171
|
+
} from '@nestjs/common';
|
|
172
|
+
import { UsersService } from '../users.service';
|
|
173
|
+
|
|
174
|
+
@Injectable()
|
|
175
|
+
export class UserExistsPipe implements PipeTransform<string, Promise<User>> {
|
|
176
|
+
constructor(private readonly usersService: UsersService) {}
|
|
177
|
+
|
|
178
|
+
async transform(id: string): Promise<User> {
|
|
179
|
+
const user = await this.usersService.findOne(id);
|
|
180
|
+
|
|
181
|
+
if (!user) {
|
|
182
|
+
throw new NotFoundException(`User with ID ${id} not found`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return user;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Usage - returns User directly, not ID
|
|
190
|
+
@Get(':id')
|
|
191
|
+
findOne(@Param('id', UserExistsPipe) user: User) {
|
|
192
|
+
return user;
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Trim and Sanitize
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// pipes/trim.pipe.ts
|
|
200
|
+
import { PipeTransform, Injectable } from '@nestjs/common';
|
|
201
|
+
|
|
202
|
+
@Injectable()
|
|
203
|
+
export class TrimPipe implements PipeTransform {
|
|
204
|
+
transform(value: unknown) {
|
|
205
|
+
if (typeof value === 'string') {
|
|
206
|
+
return value.trim();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (typeof value === 'object' && value !== null) {
|
|
210
|
+
return this.trimObject(value);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return value;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private trimObject(obj: Record<string, unknown>): Record<string, unknown> {
|
|
217
|
+
const trimmed: Record<string, unknown> = {};
|
|
218
|
+
|
|
219
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
220
|
+
trimmed[key] = typeof val === 'string' ? val.trim() : val;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return trimmed;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### File Validation Pipe
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
// pipes/file-validation.pipe.ts
|
|
232
|
+
import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';
|
|
233
|
+
|
|
234
|
+
interface FileValidationOptions {
|
|
235
|
+
maxSize: number;
|
|
236
|
+
allowedMimeTypes: string[];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
@Injectable()
|
|
240
|
+
export class FileValidationPipe implements PipeTransform {
|
|
241
|
+
constructor(private readonly options: FileValidationOptions) {}
|
|
242
|
+
|
|
243
|
+
transform(file: Express.Multer.File) {
|
|
244
|
+
if (!file) {
|
|
245
|
+
throw new BadRequestException('File is required');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (file.size > this.options.maxSize) {
|
|
249
|
+
throw new BadRequestException(
|
|
250
|
+
`File size exceeds ${this.options.maxSize / 1024 / 1024}MB`,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (!this.options.allowedMimeTypes.includes(file.mimetype)) {
|
|
255
|
+
throw new BadRequestException(
|
|
256
|
+
`File type ${file.mimetype} is not allowed`,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return file;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Usage
|
|
265
|
+
@Post('upload')
|
|
266
|
+
@UseInterceptors(FileInterceptor('file'))
|
|
267
|
+
upload(
|
|
268
|
+
@UploadedFile(
|
|
269
|
+
new FileValidationPipe({
|
|
270
|
+
maxSize: 5 * 1024 * 1024,
|
|
271
|
+
allowedMimeTypes: ['image/jpeg', 'image/png'],
|
|
272
|
+
}),
|
|
273
|
+
)
|
|
274
|
+
file: Express.Multer.File,
|
|
275
|
+
) {
|
|
276
|
+
return this.uploadService.upload(file);
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Pipe Binding Scopes
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
// Parameter level
|
|
284
|
+
@Get(':id')
|
|
285
|
+
findOne(@Param('id', ParseIntPipe) id: number) {}
|
|
286
|
+
|
|
287
|
+
// Method level
|
|
288
|
+
@Post()
|
|
289
|
+
@UsePipes(ValidationPipe)
|
|
290
|
+
create(@Body() dto: CreateDto) {}
|
|
291
|
+
|
|
292
|
+
// Controller level
|
|
293
|
+
@Controller('users')
|
|
294
|
+
@UsePipes(TrimPipe)
|
|
295
|
+
export class UsersController {}
|
|
296
|
+
|
|
297
|
+
// Global level (main.ts)
|
|
298
|
+
app.useGlobalPipes(new ValidationPipe());
|
|
299
|
+
|
|
300
|
+
// Global with DI
|
|
301
|
+
@Module({
|
|
302
|
+
providers: [
|
|
303
|
+
{
|
|
304
|
+
provide: APP_PIPE,
|
|
305
|
+
useClass: ValidationPipe,
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
})
|
|
309
|
+
export class AppModule {}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Anti-patterns
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
// BAD: Pipe with side effects
|
|
316
|
+
@Injectable()
|
|
317
|
+
export class LoggingPipe implements PipeTransform {
|
|
318
|
+
transform(value: unknown) {
|
|
319
|
+
console.log(value); // Use interceptor instead
|
|
320
|
+
this.analyticsService.track(value); // Side effect!
|
|
321
|
+
return value;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// GOOD: Pipes are for transformation/validation only
|
|
326
|
+
|
|
327
|
+
// BAD: Heavy async operations in pipe
|
|
328
|
+
@Injectable()
|
|
329
|
+
export class HeavyPipe implements PipeTransform {
|
|
330
|
+
async transform(value: string) {
|
|
331
|
+
await this.externalApi.validate(value); // Too slow
|
|
332
|
+
return value;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// GOOD: Keep pipes lightweight, use guards for auth checks
|
|
337
|
+
|
|
338
|
+
// BAD: Not handling validation errors properly
|
|
339
|
+
transform(value: string) {
|
|
340
|
+
return new Date(value); // Crashes on invalid input
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// GOOD: Proper error handling
|
|
344
|
+
transform(value: string) {
|
|
345
|
+
const date = new Date(value);
|
|
346
|
+
if (isNaN(date.getTime())) {
|
|
347
|
+
throw new BadRequestException('Invalid date');
|
|
348
|
+
}
|
|
349
|
+
return date;
|
|
350
|
+
}
|
|
351
|
+
```
|