mirror of
https://github.com/samiyev/puaros.git
synced 2025-12-28 07:16:53 +05:00
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:
72
packages/guardian/src/shared/constants/index.ts
Normal file
72
packages/guardian/src/shared/constants/index.ts
Normal 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"
|
||||
126
packages/guardian/src/shared/constants/rules.ts
Normal file
126
packages/guardian/src/shared/constants/rules.ts
Normal 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
|
||||
46
packages/guardian/src/shared/errors/BaseError.ts
Normal file
46
packages/guardian/src/shared/errors/BaseError.ts
Normal 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)
|
||||
}
|
||||
}
|
||||
4
packages/guardian/src/shared/index.ts
Normal file
4
packages/guardian/src/shared/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from "./types/Result"
|
||||
export * from "./errors/BaseError"
|
||||
export * from "./utils/Guards"
|
||||
export * from "./constants"
|
||||
29
packages/guardian/src/shared/types/Result.ts
Normal file
29
packages/guardian/src/shared/types/Result.ts
Normal 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)
|
||||
46
packages/guardian/src/shared/utils/Guards.ts
Normal file
46
packages/guardian/src/shared/utils/Guards.ts
Normal 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user