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,72 @@
export const APP_CONSTANTS = {
DEFAULT_TIMEOUT: 5000,
MAX_RETRIES: 3,
VERSION: "0.0.1",
} as const
export const ERROR_MESSAGES = {
VALIDATION_FAILED: "Validation failed",
NOT_FOUND: "Resource not found",
UNAUTHORIZED: "Unauthorized access",
INTERNAL_ERROR: "Internal server error",
FAILED_TO_ANALYZE: "Failed to analyze project",
FAILED_TO_SCAN_DIR: "Failed to scan directory",
FAILED_TO_READ_FILE: "Failed to read file",
ENTITY_NOT_FOUND: "Entity with id {id} not found",
} as const
/**
* Error codes
*/
export const ERROR_CODES = {
VALIDATION_ERROR: "VALIDATION_ERROR",
NOT_FOUND: "NOT_FOUND",
UNAUTHORIZED: "UNAUTHORIZED",
INTERNAL_ERROR: "INTERNAL_ERROR",
} as const
/**
* File extension constants
*/
export const FILE_EXTENSIONS = {
TYPESCRIPT: ".ts",
TYPESCRIPT_JSX: ".tsx",
JAVASCRIPT: ".js",
JAVASCRIPT_JSX: ".jsx",
} as const
/**
* TypeScript primitive type names
*/
export const TYPE_NAMES = {
STRING: "string",
NUMBER: "number",
BOOLEAN: "boolean",
OBJECT: "object",
} as const
/**
* Common regex patterns
*/
export const REGEX_PATTERNS = {
IMPORT_STATEMENT: /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g,
EXPORT_STATEMENT: /export\s+(?:class|function|const|let|var)\s+(\w+)/g,
} as const
/**
* Placeholders for string templates
*/
export const PLACEHOLDERS = {
ID: "{id}",
} as const
/**
* Violation severity levels
*/
export const SEVERITY_LEVELS = {
ERROR: "error",
WARNING: "warning",
INFO: "info",
} as const
export * from "./rules"

View File

@@ -0,0 +1,126 @@
/**
* Rule names for code analysis
*/
export const RULES = {
CLEAN_ARCHITECTURE: "clean-architecture",
HARDCODED_VALUE: "hardcoded-value",
CIRCULAR_DEPENDENCY: "circular-dependency",
NAMING_CONVENTION: "naming-convention",
} as const
/**
* Hardcode types
*/
export const HARDCODE_TYPES = {
MAGIC_NUMBER: "magic-number",
MAGIC_STRING: "magic-string",
MAGIC_CONFIG: "magic-config",
} as const
/**
* Layer names
*/
export const LAYERS = {
DOMAIN: "domain",
APPLICATION: "application",
INFRASTRUCTURE: "infrastructure",
SHARED: "shared",
} as const
/**
* Naming convention violation types
*/
export const NAMING_VIOLATION_TYPES = {
WRONG_SUFFIX: "wrong-suffix",
WRONG_PREFIX: "wrong-prefix",
WRONG_CASE: "wrong-case",
FORBIDDEN_PATTERN: "forbidden-pattern",
WRONG_VERB_NOUN: "wrong-verb-noun",
} as const
/**
* Naming patterns for each layer
*/
export const NAMING_PATTERNS = {
DOMAIN: {
ENTITY: {
pattern: /^[A-Z][a-zA-Z0-9]*\.ts$/,
description: "PascalCase noun (User.ts, Order.ts)",
forbidden: ["Dto", "Request", "Response", "Controller"],
},
SERVICE: {
pattern: /^[A-Z][a-zA-Z0-9]*Service\.ts$/,
description: "*Service suffix (UserService.ts)",
},
VALUE_OBJECT: {
pattern: /^[A-Z][a-zA-Z0-9]*\.ts$/,
description: "PascalCase noun (Email.ts, Money.ts)",
},
REPOSITORY_INTERFACE: {
pattern: /^I[A-Z][a-zA-Z0-9]*Repository\.ts$/,
description: "I*Repository prefix (IUserRepository.ts)",
},
},
APPLICATION: {
USE_CASE: {
pattern: /^[A-Z][a-z]+[A-Z][a-zA-Z0-9]*\.ts$/,
description: "Verb in PascalCase (CreateUser.ts, UpdateProfile.ts)",
examples: ["CreateUser.ts", "UpdateProfile.ts", "DeleteOrder.ts"],
},
DTO: {
pattern: /^[A-Z][a-zA-Z0-9]*(Dto|Request|Response)\.ts$/,
description: "*Dto, *Request, *Response suffix",
examples: ["UserResponseDto.ts", "CreateUserRequest.ts"],
},
MAPPER: {
pattern: /^[A-Z][a-zA-Z0-9]*Mapper\.ts$/,
description: "*Mapper suffix (UserMapper.ts)",
},
},
INFRASTRUCTURE: {
CONTROLLER: {
pattern: /^[A-Z][a-zA-Z0-9]*Controller\.ts$/,
description: "*Controller suffix (UserController.ts)",
},
REPOSITORY_IMPL: {
pattern: /^[A-Z][a-zA-Z0-9]*Repository\.ts$/,
description: "*Repository suffix (PrismaUserRepository.ts, MongoUserRepository.ts)",
},
SERVICE: {
pattern: /^[A-Z][a-zA-Z0-9]*(Service|Adapter)\.ts$/,
description: "*Service or *Adapter suffix (EmailService.ts, S3StorageAdapter.ts)",
},
},
} as const
/**
* Common verbs for use cases
*/
export const USE_CASE_VERBS = [
"Analyze",
"Create",
"Update",
"Delete",
"Get",
"Find",
"List",
"Search",
"Validate",
"Calculate",
"Generate",
"Send",
"Fetch",
"Process",
"Execute",
"Handle",
"Register",
"Authenticate",
"Authorize",
"Import",
"Export",
"Place",
"Cancel",
"Approve",
"Reject",
"Confirm",
] as const

View File

@@ -0,0 +1,46 @@
import { ERROR_CODES } from "../constants"
/**
* Error codes (re-exported for backwards compatibility)
*/
const LEGACY_ERROR_CODES = ERROR_CODES
/**
* Base error class for custom application errors
*/
export abstract class BaseError extends Error {
public readonly timestamp: Date
public readonly code: string
constructor(message: string, code: string) {
super(message)
this.name = this.constructor.name
this.code = code
this.timestamp = new Date()
Error.captureStackTrace(this, this.constructor)
}
}
export class ValidationError extends BaseError {
constructor(message: string) {
super(message, LEGACY_ERROR_CODES.VALIDATION_ERROR)
}
}
export class NotFoundError extends BaseError {
constructor(message: string) {
super(message, LEGACY_ERROR_CODES.NOT_FOUND)
}
}
export class UnauthorizedError extends BaseError {
constructor(message: string) {
super(message, LEGACY_ERROR_CODES.UNAUTHORIZED)
}
}
export class InternalError extends BaseError {
constructor(message: string) {
super(message, LEGACY_ERROR_CODES.INTERNAL_ERROR)
}
}

View File

@@ -0,0 +1,4 @@
export * from "./types/Result"
export * from "./errors/BaseError"
export * from "./utils/Guards"
export * from "./constants"

View File

@@ -0,0 +1,29 @@
/**
* Result type for handling success/failure scenarios
*/
export type Result<T, E = Error> = Success<T> | Failure<E>
export class Success<T> {
public readonly isSuccess = true
public readonly isFailure = false
constructor(public readonly value: T) {}
public static create<T>(value: T): Success<T> {
return new Success(value)
}
}
export class Failure<E> {
public readonly isSuccess = false
public readonly isFailure = true
constructor(public readonly error: E) {}
public static create<E>(error: E): Failure<E> {
return new Failure(error)
}
}
export const ok = <T>(value: T): Result<T> => new Success(value)
export const fail = <E>(error: E): Result<never, E> => new Failure(error)

View File

@@ -0,0 +1,46 @@
import { TYPE_NAMES } from "../constants"
/**
* Type guard utilities for runtime type checking
*/
export class Guards {
public static isNullOrUndefined(value: unknown): value is null | undefined {
return value === null || value === undefined
}
public static isString(value: unknown): value is string {
return typeof value === TYPE_NAMES.STRING
}
public static isNumber(value: unknown): value is number {
return typeof value === TYPE_NAMES.NUMBER && !isNaN(value as number)
}
public static isBoolean(value: unknown): value is boolean {
return typeof value === TYPE_NAMES.BOOLEAN
}
public static isObject(value: unknown): value is object {
return typeof value === TYPE_NAMES.OBJECT && value !== null && !Array.isArray(value)
}
public static isArray<T>(value: unknown): value is T[] {
return Array.isArray(value)
}
public static isEmpty(value: string | unknown[] | object | null | undefined): boolean {
if (Guards.isNullOrUndefined(value)) {
return true
}
if (Guards.isString(value) || Guards.isArray(value)) {
return value.length === 0
}
if (Guards.isObject(value)) {
return Object.keys(value).length === 0
}
return false
}
}