@corbat-tech/coding-standards-mcp 1.0.3 → 2.0.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 (99) hide show
  1. package/README.md +233 -337
  2. package/dist/agent.d.ts +5 -6
  3. package/dist/agent.d.ts.map +1 -1
  4. package/dist/agent.js +95 -217
  5. package/dist/agent.js.map +1 -1
  6. package/dist/analysis/code-analyzer.d.ts +44 -0
  7. package/dist/analysis/code-analyzer.d.ts.map +1 -0
  8. package/dist/analysis/code-analyzer.js +528 -0
  9. package/dist/analysis/code-analyzer.js.map +1 -0
  10. package/dist/errors.d.ts +58 -0
  11. package/dist/errors.d.ts.map +1 -0
  12. package/dist/errors.js +112 -0
  13. package/dist/errors.js.map +1 -0
  14. package/dist/guardrails.d.ts +35 -0
  15. package/dist/guardrails.d.ts.map +1 -0
  16. package/dist/guardrails.js +303 -0
  17. package/dist/guardrails.js.map +1 -0
  18. package/dist/index.js +1 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/logger.d.ts +36 -0
  21. package/dist/logger.d.ts.map +1 -0
  22. package/dist/logger.js +63 -0
  23. package/dist/logger.js.map +1 -0
  24. package/dist/metrics.d.ts +40 -0
  25. package/dist/metrics.d.ts.map +1 -0
  26. package/dist/metrics.js +97 -0
  27. package/dist/metrics.js.map +1 -0
  28. package/dist/profiles.d.ts +1 -1
  29. package/dist/profiles.d.ts.map +1 -1
  30. package/dist/profiles.js +239 -108
  31. package/dist/profiles.js.map +1 -1
  32. package/dist/prompts.js +1 -1
  33. package/dist/prompts.js.map +1 -1
  34. package/dist/tools/definitions.d.ts +143 -0
  35. package/dist/tools/definitions.d.ts.map +1 -0
  36. package/dist/tools/definitions.js +229 -0
  37. package/dist/tools/definitions.js.map +1 -0
  38. package/dist/tools/handlers/get-context.d.ts +12 -0
  39. package/dist/tools/handlers/get-context.d.ts.map +1 -0
  40. package/dist/tools/handlers/get-context.js +233 -0
  41. package/dist/tools/handlers/get-context.js.map +1 -0
  42. package/dist/tools/handlers/health.d.ts +11 -0
  43. package/dist/tools/handlers/health.d.ts.map +1 -0
  44. package/dist/tools/handlers/health.js +57 -0
  45. package/dist/tools/handlers/health.js.map +1 -0
  46. package/dist/tools/handlers/index.d.ts +12 -0
  47. package/dist/tools/handlers/index.d.ts.map +1 -0
  48. package/dist/tools/handlers/index.js +12 -0
  49. package/dist/tools/handlers/index.js.map +1 -0
  50. package/dist/tools/handlers/init.d.ts +12 -0
  51. package/dist/tools/handlers/init.d.ts.map +1 -0
  52. package/dist/tools/handlers/init.js +102 -0
  53. package/dist/tools/handlers/init.js.map +1 -0
  54. package/dist/tools/handlers/profiles.d.ts +11 -0
  55. package/dist/tools/handlers/profiles.d.ts.map +1 -0
  56. package/dist/tools/handlers/profiles.js +25 -0
  57. package/dist/tools/handlers/profiles.js.map +1 -0
  58. package/dist/tools/handlers/search.d.ts +12 -0
  59. package/dist/tools/handlers/search.d.ts.map +1 -0
  60. package/dist/tools/handlers/search.js +58 -0
  61. package/dist/tools/handlers/search.js.map +1 -0
  62. package/dist/tools/handlers/validate.d.ts +15 -0
  63. package/dist/tools/handlers/validate.d.ts.map +1 -0
  64. package/dist/tools/handlers/validate.js +71 -0
  65. package/dist/tools/handlers/validate.js.map +1 -0
  66. package/dist/tools/handlers/verify.d.ts +38 -0
  67. package/dist/tools/handlers/verify.d.ts.map +1 -0
  68. package/dist/tools/handlers/verify.js +172 -0
  69. package/dist/tools/handlers/verify.js.map +1 -0
  70. package/dist/tools/index.d.ts +22 -0
  71. package/dist/tools/index.d.ts.map +1 -0
  72. package/dist/tools/index.js +75 -0
  73. package/dist/tools/index.js.map +1 -0
  74. package/dist/tools/schemas.d.ts +29 -0
  75. package/dist/tools/schemas.d.ts.map +1 -0
  76. package/dist/tools/schemas.js +20 -0
  77. package/dist/tools/schemas.js.map +1 -0
  78. package/dist/tools.js +2 -2
  79. package/dist/tools.js.map +1 -1
  80. package/dist/types.d.ts +141 -71
  81. package/dist/types.d.ts.map +1 -1
  82. package/dist/types.js +92 -40
  83. package/dist/types.js.map +1 -1
  84. package/package.json +2 -2
  85. package/profiles/examples/microservice-kafka.yaml +122 -0
  86. package/profiles/examples/startup-fast.yaml +67 -0
  87. package/profiles/examples/strict-enterprise.yaml +62 -0
  88. package/profiles/templates/angular.yaml +614 -0
  89. package/profiles/templates/csharp-dotnet.yaml +529 -0
  90. package/profiles/templates/flutter.yaml +547 -0
  91. package/profiles/templates/go.yaml +1276 -0
  92. package/profiles/templates/java-spring-backend.yaml +326 -0
  93. package/profiles/templates/kotlin-spring.yaml +417 -0
  94. package/profiles/templates/nextjs.yaml +536 -0
  95. package/profiles/templates/nodejs.yaml +594 -0
  96. package/profiles/templates/python.yaml +546 -0
  97. package/profiles/templates/react.yaml +456 -0
  98. package/profiles/templates/rust.yaml +508 -0
  99. package/profiles/templates/vue.yaml +483 -0
@@ -336,3 +336,597 @@ security:
336
336
  # ├── integration/
337
337
  # ├── e2e/
338
338
  # └── fixtures/
339
+
340
+ # ----------------------------------------------------------------------------
341
+ # CODE EXAMPLES
342
+ # ----------------------------------------------------------------------------
343
+ codeExamples:
344
+ entity:
345
+ description: "Domain entity with validation"
346
+ code: |
347
+ // domain/entities/order.ts
348
+ import { OrderId } from '../value-objects/order-id.js';
349
+ import { Money } from '../value-objects/money.js';
350
+ import { OrderItem } from './order-item.js';
351
+ import { DomainError } from '../errors/domain-error.js';
352
+
353
+ export class Order {
354
+ private constructor(
355
+ public readonly id: OrderId,
356
+ public readonly customerId: string,
357
+ private readonly items: OrderItem[],
358
+ public readonly status: OrderStatus,
359
+ public readonly createdAt: Date
360
+ ) {}
361
+
362
+ static create(customerId: string, items: OrderItem[]): Order {
363
+ if (items.length === 0) {
364
+ throw new DomainError('Order must have at least one item');
365
+ }
366
+ return new Order(
367
+ OrderId.generate(),
368
+ customerId,
369
+ items,
370
+ 'PENDING',
371
+ new Date()
372
+ );
373
+ }
374
+
375
+ get total(): Money {
376
+ return this.items.reduce(
377
+ (sum, item) => sum.add(item.subtotal),
378
+ Money.zero('USD')
379
+ );
380
+ }
381
+
382
+ addItem(item: OrderItem): Order {
383
+ if (this.status !== 'PENDING') {
384
+ throw new DomainError('Cannot add items to confirmed order');
385
+ }
386
+ return new Order(
387
+ this.id,
388
+ this.customerId,
389
+ [...this.items, item],
390
+ this.status,
391
+ this.createdAt
392
+ );
393
+ }
394
+
395
+ confirm(): Order {
396
+ if (this.status !== 'PENDING') {
397
+ throw new DomainError('Order is not pending');
398
+ }
399
+ return new Order(
400
+ this.id,
401
+ this.customerId,
402
+ this.items,
403
+ 'CONFIRMED',
404
+ this.createdAt
405
+ );
406
+ }
407
+ }
408
+
409
+ valueObject:
410
+ description: "Immutable value object"
411
+ code: |
412
+ // domain/value-objects/money.ts
413
+ import { DomainError } from '../errors/domain-error.js';
414
+
415
+ export class Money {
416
+ private constructor(
417
+ public readonly amount: number,
418
+ public readonly currency: string
419
+ ) {
420
+ if (amount < 0) {
421
+ throw new DomainError('Amount cannot be negative');
422
+ }
423
+ }
424
+
425
+ static of(amount: number, currency: string): Money {
426
+ return new Money(amount, currency);
427
+ }
428
+
429
+ static zero(currency: string): Money {
430
+ return new Money(0, currency);
431
+ }
432
+
433
+ add(other: Money): Money {
434
+ this.ensureSameCurrency(other);
435
+ return new Money(this.amount + other.amount, this.currency);
436
+ }
437
+
438
+ multiply(factor: number): Money {
439
+ return new Money(this.amount * factor, this.currency);
440
+ }
441
+
442
+ equals(other: Money): boolean {
443
+ return this.amount === other.amount && this.currency === other.currency;
444
+ }
445
+
446
+ private ensureSameCurrency(other: Money): void {
447
+ if (this.currency !== other.currency) {
448
+ throw new DomainError(`Currency mismatch: ${this.currency} vs ${other.currency}`);
449
+ }
450
+ }
451
+
452
+ toString(): string {
453
+ return `${this.currency} ${this.amount.toFixed(2)}`;
454
+ }
455
+ }
456
+
457
+ useCase:
458
+ description: "Application use case with dependency injection"
459
+ code: |
460
+ // application/use-cases/create-order.use-case.ts
461
+ import { Order } from '../../domain/entities/order.js';
462
+ import { OrderRepository } from '../ports/order-repository.port.js';
463
+ import { EventPublisher } from '../ports/event-publisher.port.js';
464
+ import { Logger } from '../ports/logger.port.js';
465
+ import { CreateOrderDto } from '../dtos/create-order.dto.js';
466
+
467
+ export class CreateOrderUseCase {
468
+ constructor(
469
+ private readonly orderRepository: OrderRepository,
470
+ private readonly eventPublisher: EventPublisher,
471
+ private readonly logger: Logger
472
+ ) {}
473
+
474
+ async execute(dto: CreateOrderDto): Promise<Order> {
475
+ this.logger.info('Creating order', { customerId: dto.customerId });
476
+
477
+ const items = dto.items.map(item =>
478
+ OrderItem.create(item.productId, item.quantity, item.unitPrice)
479
+ );
480
+
481
+ const order = Order.create(dto.customerId, items);
482
+
483
+ await this.orderRepository.save(order);
484
+
485
+ await this.eventPublisher.publish({
486
+ type: 'OrderCreated',
487
+ payload: { orderId: order.id.value, customerId: order.customerId }
488
+ });
489
+
490
+ this.logger.info('Order created', { orderId: order.id.value });
491
+
492
+ return order;
493
+ }
494
+ }
495
+
496
+ controller:
497
+ description: "HTTP controller with validation"
498
+ code: |
499
+ // infrastructure/http/controllers/order.controller.ts
500
+ import { Router, Request, Response, NextFunction } from 'express';
501
+ import { z } from 'zod';
502
+ import { CreateOrderUseCase } from '../../../application/use-cases/create-order.use-case.js';
503
+ import { GetOrderUseCase } from '../../../application/use-cases/get-order.use-case.js';
504
+ import { validateBody } from '../middlewares/validation.middleware.js';
505
+
506
+ const createOrderSchema = z.object({
507
+ customerId: z.string().uuid(),
508
+ items: z.array(z.object({
509
+ productId: z.string().uuid(),
510
+ quantity: z.number().int().positive(),
511
+ unitPrice: z.number().positive()
512
+ })).min(1)
513
+ });
514
+
515
+ export class OrderController {
516
+ public readonly router = Router();
517
+
518
+ constructor(
519
+ private readonly createOrderUseCase: CreateOrderUseCase,
520
+ private readonly getOrderUseCase: GetOrderUseCase
521
+ ) {
522
+ this.router.post('/', validateBody(createOrderSchema), this.create);
523
+ this.router.get('/:id', this.getById);
524
+ }
525
+
526
+ private create = async (req: Request, res: Response, next: NextFunction) => {
527
+ try {
528
+ const order = await this.createOrderUseCase.execute(req.body);
529
+ res.status(201).json({
530
+ id: order.id.value,
531
+ status: order.status,
532
+ total: order.total.toString()
533
+ });
534
+ } catch (error) {
535
+ next(error);
536
+ }
537
+ };
538
+
539
+ private getById = async (req: Request, res: Response, next: NextFunction) => {
540
+ try {
541
+ const order = await this.getOrderUseCase.execute(req.params.id);
542
+ res.json(order);
543
+ } catch (error) {
544
+ next(error);
545
+ }
546
+ };
547
+ }
548
+
549
+ repository:
550
+ description: "Repository implementation with Prisma"
551
+ code: |
552
+ // infrastructure/database/repositories/prisma-order.repository.ts
553
+ import { PrismaClient } from '@prisma/client';
554
+ import { Order } from '../../../domain/entities/order.js';
555
+ import { OrderRepository } from '../../../application/ports/order-repository.port.js';
556
+ import { OrderMapper } from '../mappers/order.mapper.js';
557
+ import { EntityNotFoundError } from '../../../domain/errors/entity-not-found.error.js';
558
+
559
+ export class PrismaOrderRepository implements OrderRepository {
560
+ constructor(private readonly prisma: PrismaClient) {}
561
+
562
+ async save(order: Order): Promise<void> {
563
+ const data = OrderMapper.toPersistence(order);
564
+
565
+ await this.prisma.order.upsert({
566
+ where: { id: order.id.value },
567
+ create: data,
568
+ update: data
569
+ });
570
+ }
571
+
572
+ async findById(id: string): Promise<Order | null> {
573
+ const record = await this.prisma.order.findUnique({
574
+ where: { id },
575
+ include: { items: true }
576
+ });
577
+
578
+ return record ? OrderMapper.toDomain(record) : null;
579
+ }
580
+
581
+ async findByIdOrFail(id: string): Promise<Order> {
582
+ const order = await this.findById(id);
583
+ if (!order) {
584
+ throw new EntityNotFoundError('Order', id);
585
+ }
586
+ return order;
587
+ }
588
+
589
+ async findByCustomerId(customerId: string): Promise<Order[]> {
590
+ const records = await this.prisma.order.findMany({
591
+ where: { customerId },
592
+ include: { items: true },
593
+ orderBy: { createdAt: 'desc' }
594
+ });
595
+
596
+ return records.map(OrderMapper.toDomain);
597
+ }
598
+ }
599
+
600
+ test:
601
+ description: "Unit test with Vitest"
602
+ code: |
603
+ // domain/entities/order.test.ts
604
+ import { describe, it, expect } from 'vitest';
605
+ import { Order } from './order.js';
606
+ import { OrderItem } from './order-item.js';
607
+ import { Money } from '../value-objects/money.js';
608
+ import { DomainError } from '../errors/domain-error.js';
609
+
610
+ describe('Order', () => {
611
+ const createItem = (price: number, qty: number = 1) =>
612
+ OrderItem.create('product-id', qty, Money.of(price, 'USD'));
613
+
614
+ describe('create', () => {
615
+ it('should create order with items', () => {
616
+ const items = [createItem(100), createItem(50, 2)];
617
+ const order = Order.create('customer-id', items);
618
+
619
+ expect(order.customerId).toBe('customer-id');
620
+ expect(order.status).toBe('PENDING');
621
+ expect(order.total.amount).toBe(200);
622
+ });
623
+
624
+ it('should throw when creating with no items', () => {
625
+ expect(() => Order.create('customer-id', [])).toThrow(DomainError);
626
+ });
627
+ });
628
+
629
+ describe('confirm', () => {
630
+ it('should change status to CONFIRMED', () => {
631
+ const order = Order.create('customer-id', [createItem(100)]);
632
+ const confirmed = order.confirm();
633
+
634
+ expect(confirmed.status).toBe('CONFIRMED');
635
+ });
636
+
637
+ it('should throw when confirming non-pending order', () => {
638
+ const order = Order.create('customer-id', [createItem(100)]).confirm();
639
+ expect(() => order.confirm()).toThrow(DomainError);
640
+ });
641
+ });
642
+
643
+ describe('addItem', () => {
644
+ it('should add item and update total', () => {
645
+ const order = Order.create('customer-id', [createItem(100)]);
646
+ const updated = order.addItem(createItem(50));
647
+
648
+ expect(updated.total.amount).toBe(150);
649
+ });
650
+ });
651
+ });
652
+
653
+ errorHandling:
654
+ description: "Custom error classes and middleware"
655
+ code: |
656
+ // domain/errors/domain-error.ts
657
+ export class DomainError extends Error {
658
+ constructor(message: string) {
659
+ super(message);
660
+ this.name = 'DomainError';
661
+ }
662
+ }
663
+
664
+ // domain/errors/entity-not-found.error.ts
665
+ export class EntityNotFoundError extends DomainError {
666
+ constructor(entity: string, id: string) {
667
+ super(`${entity} with id ${id} not found`);
668
+ this.name = 'EntityNotFoundError';
669
+ }
670
+ }
671
+
672
+ // infrastructure/http/middlewares/error.middleware.ts
673
+ import { Request, Response, NextFunction } from 'express';
674
+ import { DomainError } from '../../../domain/errors/domain-error.js';
675
+ import { EntityNotFoundError } from '../../../domain/errors/entity-not-found.error.js';
676
+ import { ZodError } from 'zod';
677
+
678
+ export function errorMiddleware(
679
+ error: Error,
680
+ req: Request,
681
+ res: Response,
682
+ _next: NextFunction
683
+ ) {
684
+ if (error instanceof EntityNotFoundError) {
685
+ return res.status(404).json({
686
+ type: 'NotFound',
687
+ message: error.message
688
+ });
689
+ }
690
+
691
+ if (error instanceof DomainError) {
692
+ return res.status(400).json({
693
+ type: 'DomainError',
694
+ message: error.message
695
+ });
696
+ }
697
+
698
+ if (error instanceof ZodError) {
699
+ return res.status(400).json({
700
+ type: 'ValidationError',
701
+ errors: error.errors
702
+ });
703
+ }
704
+
705
+ // Log unexpected errors
706
+ console.error('Unexpected error:', error);
707
+
708
+ return res.status(500).json({
709
+ type: 'InternalError',
710
+ message: 'An unexpected error occurred'
711
+ });
712
+ }
713
+
714
+ # ----------------------------------------------------------------------------
715
+ # ANTI-PATTERNS
716
+ # ----------------------------------------------------------------------------
717
+ antiPatterns:
718
+ anyType:
719
+ name: "Using 'any' Type"
720
+ description: "Disabling TypeScript's type safety with 'any'"
721
+ bad: |
722
+ // ❌ any disables type checking
723
+ async function processData(data: any): Promise<any> {
724
+ const result = data.items.map((item: any) => item.value);
725
+ return { success: true, data: result };
726
+ }
727
+ good: |
728
+ // ✅ Proper typing
729
+ interface DataItem { value: number; }
730
+ interface ProcessInput { items: DataItem[]; }
731
+ interface ProcessResult { success: boolean; data: number[]; }
732
+
733
+ async function processData(data: ProcessInput): Promise<ProcessResult> {
734
+ const result = data.items.map(item => item.value);
735
+ return { success: true, data: result };
736
+ }
737
+
738
+ callbackHell:
739
+ name: "Callback Hell"
740
+ description: "Nested callbacks instead of async/await"
741
+ bad: |
742
+ // ❌ Callback hell
743
+ function processOrder(orderId: string, callback: (err: Error | null, result?: any) => void) {
744
+ getOrder(orderId, (err, order) => {
745
+ if (err) return callback(err);
746
+ validateOrder(order, (err, valid) => {
747
+ if (err) return callback(err);
748
+ processPayment(order, (err, payment) => {
749
+ if (err) return callback(err);
750
+ callback(null, { order, payment });
751
+ });
752
+ });
753
+ });
754
+ }
755
+ good: |
756
+ // ✅ async/await
757
+ async function processOrder(orderId: string): Promise<ProcessedOrder> {
758
+ const order = await getOrder(orderId);
759
+ await validateOrder(order);
760
+ const payment = await processPayment(order);
761
+ return { order, payment };
762
+ }
763
+
764
+ swallowingErrors:
765
+ name: "Swallowing Errors"
766
+ description: "Catching errors without proper handling"
767
+ bad: |
768
+ // ❌ Silently ignoring errors
769
+ try {
770
+ await saveOrder(order);
771
+ } catch (error) {
772
+ // Do nothing - bad!
773
+ }
774
+
775
+ // ❌ Just logging and continuing
776
+ try {
777
+ await saveOrder(order);
778
+ } catch (error) {
779
+ console.log('Error:', error);
780
+ // Continues as if nothing happened
781
+ }
782
+ good: |
783
+ // ✅ Proper error handling
784
+ try {
785
+ await saveOrder(order);
786
+ } catch (error) {
787
+ logger.error('Failed to save order', { orderId: order.id, error });
788
+ throw new ApplicationError('Order save failed', { cause: error });
789
+ }
790
+
791
+ syncFileOperations:
792
+ name: "Synchronous File Operations"
793
+ description: "Blocking the event loop with sync I/O"
794
+ bad: |
795
+ // ❌ Blocks event loop
796
+ import { readFileSync, writeFileSync } from 'fs';
797
+
798
+ function loadConfig(): Config {
799
+ const content = readFileSync('config.json', 'utf-8');
800
+ return JSON.parse(content);
801
+ }
802
+ good: |
803
+ // ✅ Async operations
804
+ import { readFile, writeFile } from 'fs/promises';
805
+
806
+ async function loadConfig(): Promise<Config> {
807
+ const content = await readFile('config.json', 'utf-8');
808
+ return JSON.parse(content);
809
+ }
810
+
811
+ businessLogicInController:
812
+ name: "Business Logic in Controllers"
813
+ description: "Putting domain logic in HTTP handlers"
814
+ bad: |
815
+ // ❌ Business logic in controller
816
+ app.post('/orders', async (req, res) => {
817
+ const { customerId, items } = req.body;
818
+
819
+ // Business rules in controller - bad!
820
+ if (items.length === 0) {
821
+ return res.status(400).json({ error: 'No items' });
822
+ }
823
+
824
+ let total = 0;
825
+ for (const item of items) {
826
+ total += item.price * item.quantity;
827
+ }
828
+
829
+ if (total > 10000) {
830
+ // Send email to manager...
831
+ }
832
+
833
+ const order = await prisma.order.create({ data: { ... } });
834
+ res.json(order);
835
+ });
836
+ good: |
837
+ // ✅ Controller delegates to use case
838
+ app.post('/orders', async (req, res, next) => {
839
+ try {
840
+ const order = await createOrderUseCase.execute(req.body);
841
+ res.status(201).json(OrderMapper.toResponse(order));
842
+ } catch (error) {
843
+ next(error);
844
+ }
845
+ });
846
+
847
+ requireInsteadOfImport:
848
+ name: "Using CommonJS require()"
849
+ description: "Using require() instead of ES modules"
850
+ bad: |
851
+ // ❌ CommonJS
852
+ const express = require('express');
853
+ const { readFile } = require('fs');
854
+
855
+ module.exports = { createServer };
856
+ good: |
857
+ // ✅ ES Modules
858
+ import express from 'express';
859
+ import { readFile } from 'fs/promises';
860
+
861
+ export { createServer };
862
+
863
+ hardcodedConfig:
864
+ name: "Hardcoded Configuration"
865
+ description: "Embedding configuration values in code"
866
+ bad: |
867
+ // ❌ Hardcoded values
868
+ const db = new PrismaClient({
869
+ datasources: {
870
+ db: { url: 'postgresql://user:pass@localhost:5432/mydb' }
871
+ }
872
+ });
873
+
874
+ const jwtSecret = 'my-super-secret-key';
875
+ good: |
876
+ // ✅ Environment variables with validation
877
+ import { z } from 'zod';
878
+
879
+ const envSchema = z.object({
880
+ DATABASE_URL: z.string().url(),
881
+ JWT_SECRET: z.string().min(32),
882
+ NODE_ENV: z.enum(['development', 'production', 'test'])
883
+ });
884
+
885
+ export const config = envSchema.parse(process.env);
886
+
887
+ nPlusOneQueries:
888
+ name: "N+1 Query Problem"
889
+ description: "Making separate database queries in a loop"
890
+ bad: |
891
+ // ❌ N+1 queries
892
+ const orders = await prisma.order.findMany();
893
+ for (const order of orders) {
894
+ order.customer = await prisma.customer.findUnique({
895
+ where: { id: order.customerId }
896
+ });
897
+ }
898
+ good: |
899
+ // ✅ Use include for eager loading
900
+ const orders = await prisma.order.findMany({
901
+ include: { customer: true }
902
+ });
903
+
904
+ // ✅ Or batch with findMany
905
+ const orders = await prisma.order.findMany();
906
+ const customerIds = [...new Set(orders.map(o => o.customerId))];
907
+ const customers = await prisma.customer.findMany({
908
+ where: { id: { in: customerIds } }
909
+ });
910
+
911
+ mutatingParameters:
912
+ name: "Mutating Function Parameters"
913
+ description: "Modifying input objects instead of returning new ones"
914
+ bad: |
915
+ // ❌ Mutating input
916
+ function processOrder(order: Order): void {
917
+ order.status = 'PROCESSED';
918
+ order.processedAt = new Date();
919
+ order.items.forEach(item => {
920
+ item.processed = true;
921
+ });
922
+ }
923
+ good: |
924
+ // ✅ Return new object (immutability)
925
+ function processOrder(order: Order): Order {
926
+ return {
927
+ ...order,
928
+ status: 'PROCESSED',
929
+ processedAt: new Date(),
930
+ items: order.items.map(item => ({ ...item, processed: true }))
931
+ };
932
+ }