feat(guardian): add guardian package - code quality analyzer

Add @puaros/guardian package v0.1.0 - code quality guardian for vibe coders and enterprise teams.

Features:
- Hardcode detection (magic numbers, magic strings)
- Circular dependency detection
- Naming convention enforcement (Clean Architecture)
- Architecture violation detection
- CLI tool with comprehensive reporting
- 159 tests with 80%+ coverage
- Smart suggestions for fixes
- Built for AI-assisted development

Built with Clean Architecture and DDD principles.
Works with Claude, GPT, Copilot, Cursor, and any AI coding assistant.
This commit is contained in:
imfozilbek
2025-11-24 02:54:39 +05:00
parent 9f97509b06
commit 03705b5264
96 changed files with 9520 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
import { ValueObject } from "../../../../src/domain/value-objects/ValueObject"
interface EmailProps {
readonly value: string
}
/**
* Email Value Object
*
* DDD Pattern: Value Object
* - Immutable
* - Self-validating
* - No identity
* - Equality by value
*
* Clean Code:
* - Single Responsibility: represents email
* - Meaningful name: clearly email
* - No magic values: validation rules as constants
*/
export class Email extends ValueObject<EmailProps> {
private static readonly EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
private static readonly MAX_LENGTH = 255
private constructor(props: EmailProps) {
super(props)
}
public static create(email: string): Email {
const trimmed = email.trim().toLowerCase()
if (!trimmed) {
throw new Error("Email cannot be empty")
}
if (trimmed.length > Email.MAX_LENGTH) {
throw new Error(`Email must be less than ${Email.MAX_LENGTH} characters`)
}
if (!Email.EMAIL_REGEX.test(trimmed)) {
throw new Error(`Invalid email format: ${email}`)
}
return new Email({ value: trimmed })
}
public get value(): string {
return this.props.value
}
public getDomain(): string {
return this.props.value.split("@")[1]
}
public isFromDomain(domain: string): boolean {
return this.getDomain() === domain.toLowerCase()
}
public toString(): string {
return this.props.value
}
}

View File

@@ -0,0 +1,107 @@
import { ValueObject } from "../../../../src/domain/value-objects/ValueObject"
interface MoneyProps {
readonly amount: number
readonly currency: string
}
/**
* Money Value Object
*
* DDD Pattern: Value Object
* - Encapsulates amount + currency
* - Immutable
* - Rich behavior (add, subtract, compare)
*
* Prevents common bugs:
* - Adding different currencies
* - Negative amounts (when not allowed)
* - Floating point precision issues
*/
export class Money extends ValueObject<MoneyProps> {
private static readonly SUPPORTED_CURRENCIES = ["USD", "EUR", "GBP", "RUB"]
private static readonly DECIMAL_PLACES = 2
private constructor(props: MoneyProps) {
super(props)
}
public static create(amount: number, currency: string): Money {
const upperCurrency = currency.toUpperCase()
if (!Money.SUPPORTED_CURRENCIES.includes(upperCurrency)) {
throw new Error(
`Unsupported currency: ${currency}. Supported: ${Money.SUPPORTED_CURRENCIES.join(", ")}`,
)
}
if (amount < 0) {
throw new Error("Money amount cannot be negative")
}
const rounded = Math.round(amount * 100) / 100
return new Money({ amount: rounded, currency: upperCurrency })
}
public static zero(currency: string): Money {
return Money.create(0, currency)
}
public get amount(): number {
return this.props.amount
}
public get currency(): string {
return this.props.currency
}
public add(other: Money): Money {
this.ensureSameCurrency(other)
return Money.create(this.amount + other.amount, this.currency)
}
public subtract(other: Money): Money {
this.ensureSameCurrency(other)
const result = this.amount - other.amount
if (result < 0) {
throw new Error("Cannot subtract: result would be negative")
}
return Money.create(result, this.currency)
}
public multiply(multiplier: number): Money {
if (multiplier < 0) {
throw new Error("Multiplier cannot be negative")
}
return Money.create(this.amount * multiplier, this.currency)
}
public isGreaterThan(other: Money): boolean {
this.ensureSameCurrency(other)
return this.amount > other.amount
}
public isLessThan(other: Money): boolean {
this.ensureSameCurrency(other)
return this.amount < other.amount
}
public isZero(): boolean {
return this.amount === 0
}
private ensureSameCurrency(other: Money): void {
if (this.currency !== other.currency) {
throw new Error(
`Cannot operate on different currencies: ${this.currency} vs ${other.currency}`,
)
}
}
public toString(): string {
return `${this.amount.toFixed(Money.DECIMAL_PLACES)} ${this.currency}`
}
}

View File

@@ -0,0 +1,35 @@
import { ValueObject } from "../../../../src/domain/value-objects/ValueObject"
import { v4 as uuidv4, validate as uuidValidate } from "uuid"
interface OrderIdProps {
readonly value: string
}
/**
* OrderId Value Object
*
* Type safety: cannot mix with UserId
*/
export class OrderId extends ValueObject<OrderIdProps> {
private constructor(props: OrderIdProps) {
super(props)
}
public static create(id?: string): OrderId {
const value = id ?? uuidv4()
if (!uuidValidate(value)) {
throw new Error(`Invalid OrderId format: ${value}`)
}
return new OrderId({ value })
}
public get value(): string {
return this.props.value
}
public toString(): string {
return this.props.value
}
}

View File

@@ -0,0 +1,92 @@
import { ValueObject } from "../../../../src/domain/value-objects/ValueObject"
interface OrderStatusProps {
readonly value: string
}
/**
* OrderStatus Value Object
*
* DDD Pattern: Enum as Value Object
* - Type-safe status
* - Business logic: valid transitions
* - Self-validating
*/
export class OrderStatus extends ValueObject<OrderStatusProps> {
public static readonly PENDING = new OrderStatus({ value: "pending" })
public static readonly CONFIRMED = new OrderStatus({ value: "confirmed" })
public static readonly PAID = new OrderStatus({ value: "paid" })
public static readonly SHIPPED = new OrderStatus({ value: "shipped" })
public static readonly DELIVERED = new OrderStatus({ value: "delivered" })
public static readonly CANCELLED = new OrderStatus({ value: "cancelled" })
private static readonly VALID_STATUSES = [
"pending",
"confirmed",
"paid",
"shipped",
"delivered",
"cancelled",
]
private constructor(props: OrderStatusProps) {
super(props)
}
public static create(status: string): OrderStatus {
const lower = status.toLowerCase()
if (!OrderStatus.VALID_STATUSES.includes(lower)) {
throw new Error(
`Invalid order status: ${status}. Valid: ${OrderStatus.VALID_STATUSES.join(", ")}`,
)
}
return new OrderStatus({ value: lower })
}
public get value(): string {
return this.props.value
}
/**
* Business Rule: Valid status transitions
*/
public canTransitionTo(newStatus: OrderStatus): boolean {
const transitions: Record<string, string[]> = {
pending: ["confirmed", "cancelled"],
confirmed: ["paid", "cancelled"],
paid: ["shipped", "cancelled"],
shipped: ["delivered"],
delivered: [],
cancelled: [],
}
const allowedTransitions = transitions[this.value] ?? []
return allowedTransitions.includes(newStatus.value)
}
public isPending(): boolean {
return this.value === "pending"
}
public isConfirmed(): boolean {
return this.value === "confirmed"
}
public isCancelled(): boolean {
return this.value === "cancelled"
}
public isDelivered(): boolean {
return this.value === "delivered"
}
public isFinal(): boolean {
return this.isDelivered() || this.isCancelled()
}
public toString(): string {
return this.props.value
}
}

View File

@@ -0,0 +1,43 @@
import { ValueObject } from "../../../../src/domain/value-objects/ValueObject"
import { v4 as uuidv4, validate as uuidValidate } from "uuid"
interface UserIdProps {
readonly value: string
}
/**
* UserId Value Object
*
* DDD Pattern: Identity Value Object
* - Strongly typed ID (not just string)
* - Self-validating
* - Type safety: can't mix with OrderId
*
* Benefits:
* - No accidental ID mixing: `findUser(orderId)` won't compile
* - Clear intent in code
* - Encapsulated validation
*/
export class UserId extends ValueObject<UserIdProps> {
private constructor(props: UserIdProps) {
super(props)
}
public static create(id?: string): UserId {
const value = id ?? uuidv4()
if (!uuidValidate(value)) {
throw new Error(`Invalid UserId format: ${value}`)
}
return new UserId({ value })
}
public get value(): string {
return this.props.value
}
public toString(): string {
return this.props.value
}
}