@angular-modernizer/core
v1.2.0
Published
Core kernel for the Angular Modernization Platform
Maintainers
Readme
@angular-modernizer/core
The Kernel - Infrastructure and plugin lifecycle management
Overview
The @angular-modernizer/core package provides the foundational infrastructure for the Angular Modernization Platform. It contains the Kernel, file system abstractions, plugin management system, transformation pipeline, and advanced orchestration capabilities that power the entire platform.
Architecture
The core package follows the "Infrastructure Only" principle - it provides domain-agnostic building blocks without any Angular-specific logic.
Kernel (Domain Agnostic)
- Plugin System (Contracts)
- File System (Abstraction)
- TypeScript Project (ts-morph)
- Transformation Pipeline (Coordinator + Execution Engine)
- Orchestration System (Parallel + Incremental + Workers)
- Cache Manager (AST Caching + Invalidation)Core Components
Kernel
The Kernel is the central orchestrator that manages the entire platform lifecycle:
interface KernelConfig {
tsConfigPath?: string;
fileSystem?: FileSystemAdapter;
projectOptions?: Partial<ProjectOptions>;
plugins?: Plugin[];
transformationCoordinator?: TransformationCoordinator;
api?: unknown; // defaults to createPublicApi(project) when plugins are set
cacheConfig?: Partial<CacheConfig>;
resourceLimits?: ResourceLimits;
enableParallelExecution?: boolean; // default: true
}
class Kernel {
constructor(config?: KernelConfig); // All parameters optional
async initialize(): Promise<void>;
getProject(): Project;
getFileSystem(): FileSystemAdapter;
getCacheManager(): CacheManager | undefined;
getBuildValidator(): BuildValidator;
getPlugin(name: string): Plugin | undefined;
getAllPlugins(): Plugin[];
hasPlugin(name: string): boolean;
isInitialized(): boolean;
getConfig(): Readonly<KernelConfig>;
getProjectRoot(): string; // safe before initialize()
// Transformation API
transformFile(
sourceFile: SourceFile,
config?: TransformationConfig,
): Promise<TransformationResult>;
// Orchestration API
getParallelExecutor(): ParallelExecutor | undefined;
getIncrementalAnalyzer(): IncrementalAnalyzer | undefined;
getProgressReporter(): ProgressReporter | undefined;
getResourceManager(): ResourceManager | undefined;
getOrchestrationStats(): OrchestrationStats | undefined;
analyzeParallel(options?: ParallelAnalysisOptions): Promise<unknown[]>;
analyzeIncremental(options?: IncrementalAnalysisOptions): Promise<unknown>;
}Key features:
- All constructor parameters are optional with sensible defaults
- Async lifecycle management with async plugin loading
- Full TypeScript support with strict typing
- Configuration is read-only after initialization
Usage examples:
// Minimal setup
const kernel1 = new Kernel();
await kernel1.initialize();
// Explicit empty config
const kernel2 = new Kernel({});
await kernel2.initialize();
// Full configuration
const kernel3 = new Kernel({
tsConfigPath: './tsconfig.json',
fileSystem: new RealFileSystemAdapter(),
plugins: [new StandalonePlugin()],
});
await kernel3.initialize();File System Abstraction
The platform uses a custom FileSystemAdapter interface to enable both production file system access and in-memory testing:
interface FileSystemAdapter {
readFile(path: string): Promise<string>;
writeFile(path: string, content: string): Promise<void>;
exists(path: string): Promise<boolean>;
readDirectory(path: string): Promise<string[]>;
deleteFile(path: string): Promise<void>;
createDirectory(path: string): Promise<void>;
fileExists(path: string): Promise<boolean>;
directoryExists(path: string): Promise<boolean>;
getFileStats(path: string): Promise<{ /* size, mtime, ... */ }>;
}RealFileSystemAdapter
Production file system access with full ts-morph compatibility:
import { RealFileSystemAdapter } from '@angular-modernizer/core';
const fileSystem = new RealFileSystemAdapter();
const kernel = new Kernel({
fileSystem,
projectOptions: {
fileSystem: fileSystem,
},
});Features:
- Full
FileSystemHostimplementation for ts-morph compatibility - All sync methods implemented:
readFileSync(),writeFileSync(),mkdirSync(), etc. - Proper error handling and encoding support
InMemoryFileSystemAdapter
Testing and development with in-memory file system:
import { InMemoryFileSystemAdapter } from '@angular-modernizer/core';
const fileSystem = new InMemoryFileSystemAdapter();
const kernel = new Kernel({
fileSystem,
projectOptions: {
useInMemoryFileSystem: true,
compilerOptions: {
target: 99, // ES2022
module: 199, // NodeNext
},
},
});Features:
- No file system side effects
- Fast and deterministic
- Full ts-morph compatibility
Plugin Management
The Kernel provides comprehensive plugin lifecycle management:
class Kernel {
getPlugin(name: string): Plugin | undefined;
getAllPlugins(): Plugin[];
hasPlugin(name: string): boolean;
async initialize(): Promise<void>;
}Plugin interface:
interface Plugin {
readonly name: string;
readonly version: string;
initialize(kernel: IKernel): Promise<void>;
getAnalysisRules(): AnalysisRule[];
getTransformRules(): TransformRule[];
getValidationRules(): ValidationRule[];
}Transformation Infrastructure
The core package provides a robust transformation pipeline for automated code modifications.
TransformationCoordinator
Orchestrates transformation operations across multiple plugins:
class TransformationCoordinator {
constructor(plugins: Plugin[], api: unknown);
async transformFile(
sourceFile: SourceFile,
project: Project,
config?: TransformationConfig,
): Promise<TransformationResult>;
}Key responsibilities:
- Plugin discovery (finds all
TransformRules across loaded plugins) - Context creation (builds
TransformContextwith dependency injection) - Rule execution (coordinates execution through
TransformExecutionEngine) - Result aggregation (combines results from multiple rules)
- Error isolation (failed rules don't stop the entire transformation)
TransformExecutionEngine
Safely executes individual transformation rules:
class TransformExecutionEngine {
async executeRule(
rule: TransformRule,
context: TransformContext,
): Promise<TransformResult>;
}Key features:
- Pre-execution validation of rules and contexts
- 30-second execution timeout prevents hanging
- Structured error results instead of exceptions
- Consistent result format across all rules
TransformContext and TransformResult
interface TransformContext {
sourceFile: SourceFile;
project: Project;
api?: unknown;
config?: Record<string, unknown>;
filePath?: string;
type?: string;
options?: {
dryRun?: boolean;
autoSave?: boolean;
format?: boolean;
};
}
interface TransformResult {
ruleId: string;
modified: boolean;
message: string;
filePath?: string;
changeCount?: number;
metadata?: Record<string, unknown>;
}Cache Manager
Intelligent caching system for AST and analysis results with automatic invalidation.
The CacheManager provides significant performance improvements by caching expensive operations. It automatically invalidates cache entries when files change, using SHA-256 content hashing for accuracy.
interface CacheConfig {
enabled: boolean;
cacheDir: string;
maxSize: number;
maxAge: number;
version: string;
compression: boolean;
}
class CacheManager {
constructor(fileSystem: FileSystemAdapter, config?: Partial<CacheConfig>);
async initialize(): Promise<void>;
async getAst(filePath: string): Promise<CachedAst | null>;
async setAst(filePath: string, ast: string): Promise<void>;
async getAnalysisResult(filePath: string): Promise<CachedAnalysisResult | null>;
async setAnalysisResult(
filePath: string,
results: unknown[],
dependencies?: string[],
): Promise<void>;
async invalidate(filePath: string, type?: 'ast' | 'analysis'): Promise<void>;
async clear(): Promise<void>;
getStats(): CacheStats;
}Key features:
- Content-based invalidation using SHA-256 hashes
- LRU eviction when cache size limits are reached; eviction,
clear()and size statistics touch only the manager's own entries, never other caches or files in the same directory - Entries at
<cacheDir>/(ast|analysis)/<name>.<hash>.(ast|analysis).json, independent of the date, somaxAge(24 hours) applies across midnight - Dependency tracking (invalidates analysis results when dependencies change)
- Optional gzip compression to reduce disk usage
- Real-time hit rate and performance metrics
Performance characteristics:
| Operation | Performance | Notes | |-|-|-| | Cache Hit | 50,657 ops/sec | 1.58x faster than writes | | Cache Miss | 208,606 ops/sec | Ultra-fast null return | | File Change Detection | 52,052 ops/sec | Unchanged files | | Write with Compression | 60,157 ops/sec | 10KB with gzip | | Statistics | 2.8M ops/sec | Nearly instant |
Usage example:
const kernel = new Kernel({
tsConfigPath: './tsconfig.json',
fileSystem: new RealFileSystemAdapter(),
cacheConfig: {
enabled: true,
cacheDir: '.angular-modernizer-cache',
maxSize: 100 * 1024 * 1024, // 100MB
maxAge: 24 * 60 * 60 * 1000, // 24 hours
compression: true,
version: '1.0.0',
},
});
await kernel.initialize();
const cache = kernel.getCacheManager();
if (cache) {
const stats = cache.getStats();
console.log(`Hit rate: ${stats.hitRate.toFixed(1)}%`);
console.log(`Total entries: ${stats.totalEntries}`);
}For detailed caching documentation, see CACHE.md.
Orchestration System
Advanced parallel processing, incremental analysis, and resource management for large-scale Angular projects.
interface KernelConfig {
resourceLimits?: {
maxMemory?: number; // bytes, default: 80% of available
maxWorkers?: number; // default: CPU cores - 1
maxQueueSize?: number; // default: 1000
autoThrottle?: boolean; // default: true
};
enableParallelExecution?: boolean; // default: true
}Key components:
ParallelExecutor - File-level parallelism with configurable worker pools. Supports both Promise-based execution and worker threads for true multi-threaded execution.
IncrementalAnalyzer - Git-based change detection for efficient re-analysis. Uses content hashing for file modification tracking and dependency graph analysis for transitive invalidation.
ProgressReporter - Real-time progress tracking with ETA calculation. Event-driven architecture for flexible integration.
ResourceManager - Memory monitoring with configurable heap limits and CPU usage tracking. Automatic resource management for large codebases.
WorkerPool - Node.js worker threads for true parallelism. Isolated V8 instances preventing memory interference. Health monitoring with automatic recycling.
Performance characteristics:
| Feature | Performance | Use Case | |-|-|-| | Parallel Execution | 1.5-4x speedup | Large codebases (100+ files) | | Incremental Analysis | >90% cache hit rate | Repeated analysis runs | | Worker Threads | 5-10x potential speedup | CPU-intensive tasks | | Progress Reporting | Real-time with ETA | Long-running operations | | Resource Management | Automatic throttling | Memory-constrained environments |
Usage examples:
// Parallel analysis over all project files and the rules of all plugins
const kernel = new Kernel({
tsConfigPath: './tsconfig.json',
plugins: [new SolidPlugin()],
resourceLimits: { maxWorkers: 4 },
});
await kernel.initialize();
const results = await kernel.analyzeParallel({
plugins: ['@angular-modernizer/plugin-solid'],
onProgress: (progress) => {
console.log(`${progress.percentage}% complete, ETA: ${progress.eta}ms`);
},
});
// Incremental analysis against a git base
const incrementalResult = await kernel.analyzeIncremental({
baseBranch: 'main',
includeDependents: true,
});
// Worker thread execution
const workerResults = await kernel.analyzeParallel({
useWorkerThreads: true,
tsConfigPath: './tsconfig.json',
workerScriptPath: getWorkerScriptPath(), // from @angular-modernizer/worker
maxWorkers: 4,
});For detailed orchestration documentation, see the orchestration source files in src/orchestration/.
Feedback Collection
Privacy-first AI transformation feedback system for continuous learning and improvement.
The feedback collection system captures transformation outcomes, AI decision-making, and validation results in a privacy-preserving format that enables pattern learning without exposing sensitive code or business logic.
Key features:
- No code snippets stored, hashed file paths, bucketed metrics
- Dual storage (project
.claude-feedback/and platform root) - Automated validation with 6 checks ensuring feedback quality
- Outlier detection (4 detection types) for anomaly handling
- Pattern detection with confidence scoring
FeedbackCollector
import { FeedbackCollector } from '@angular-modernizer/core';
const collector = new FeedbackCollector({
projectRoot: '/path/to/project',
platformRoot: '/path/to/tsanalyzer',
storageLocation: 'both', // 'project', 'platform', or 'both' (default)
salt: 'unique-installation-salt',
});
const project = new Project({ tsConfigFilePath: 'tsconfig.json' });
const sessionId = await collector.startSession({ projectRoot: '/path/to/project', project });
const feedback: TransformationFeedback = {
transformationId: 'transform-1',
timestamp: new Date().toISOString(),
rule: {
ruleId: 'standalone-migration-v1',
ruleName: 'Migrate to Standalone Components',
ruleType: 'transform',
category: 'standalone-migration',
},
target: {
filePathHash: 'src/app/user.component.ts', // Will be hashed
fileType: 'component',
linesOfCode: 147,
complexity: 8.5,
},
decision: {
approach: 'Convert to standalone component',
alternatives: ['Keep NgModule', 'Partial migration'],
reasoning: 'File is isolated, no complex dependencies',
confidence: 0.95,
contextUsed: ['file-structure', 'dependencies'],
timeToDecision: 250,
},
execution: {
succeeded: true,
duration: 1200,
},
validation: {
build: { success: true, duration: 8500, errors: [], warnings: [] },
tests: { success: true, duration: 4200, coverage: 92.5 },
},
outcome: {
status: 'success',
quality: 'improved',
metrics: {
codeComplexity: { before: 10, after: 8, delta: -2 },
},
},
metadata: {
source: 'automatic',
collectorVersion: '1.0.0',
reliability: 1.0,
completenessScore: 95,
},
};
try {
await collector.recordTransformation(sessionId, feedback);
} catch (error) {
console.error('Validation failed:', error.message);
}
const outcome = await collector.endSession(sessionId);
console.log(`Success: ${outcome.successful}/${outcome.totalTransformations}`);API reference:
class FeedbackCollector {
constructor(
config: FeedbackCollectorConfig,
privacyFilter?: PrivacyFilter,
feedbackStorage?: FeedbackStorage,
feedbackValidator?: FeedbackValidator,
);
async startSession(context: TransformContext): Promise<string>;
async recordTransformation(sessionId: string, feedback: TransformationFeedback): Promise<void>;
async endSession(sessionId: string): Promise<SessionOutcome>;
extractLearnings(feedback: TransformationFeedback): Learning[];
}PrivacyFilter
Anonymizes sensitive data before storage:
import { PrivacyFilter } from '@angular-modernizer/core';
const filter = new PrivacyFilter({
salt: 'unique-installation-salt',
strictMode: true,
});
const anonymized = filter.anonymize(feedback);
// File path hashing (preserves structure)
const hashedPath = filter.anonymizeFilePath('src/app/user/user.component.ts', 'salt');
// Result: "abc123/def456/ghi789/jkl012.component.ts"
// Bucket numeric values (hide exact numbers)
const complexityBucket = filter.bucketValue(8.5, [0, 5, 10, 20, 50]);
// Result: "small" (labels: tiny, small, medium, large, huge)
// Audit privacy before storage (returns a PrivacyAudit, does not throw)
const audit = filter.auditPrivacy(anonymized);
if (!audit.passed) {
console.error('Privacy audit failed:', {
hasCodeSnippets: audit.hasCodeSnippets,
hasRealFilePaths: audit.hasRealFilePaths,
hasProjectNames: audit.hasProjectNames,
hasBusinessLogic: audit.hasBusinessLogic,
});
}FeedbackValidator
Validates feedback quality with 6 checks:
import { FeedbackValidator } from '@angular-modernizer/core';
const validator = new FeedbackValidator({
minCompletenessScore: 70,
strictMode: false,
maxDuration: 600000,
});
const result = validator.validate(feedback);
if (!result.valid) {
console.error(`Validation failed (score: ${result.score}/100):`);
result.issues.forEach((issue) => {
console.error(` [${issue.severity}] ${issue.field}: ${issue.message}`);
});
}Validation checks:
- Schema completeness (required fields present)
- Consistency checks (outcome matches execution)
- Confidence sanity (high confidence failures flagged)
- Quality metrics validity (reasonable deltas)
- Duration sanity (positive, reasonable durations)
- Learning quality (patterns, evidence present)
OutlierDetector
Detects anomalous feedback for quarantine:
import { OutlierDetector } from '@angular-modernizer/core';
const detector = new OutlierDetector();
const report = detector.detectOutliers(feedbackArray);
console.log(`Outlier rate: ${report.outlierRate * 100}%`);
report.outliers.forEach((outlier) => {
console.log(`${outlier.feedbackId}: ${outlier.reasons.join(', ')}`);
console.log(`Action: ${outlier.action}`); // 'quarantine' | 'flag' | 'purge'
});Four outlier detection types:
- Duration outliers: >10x median duration (unusually slow)
- Overconfident failures: AI confidence >0.95 but transformation failed
- Quality degradation: code complexity increased >50%
- Impossibly fast: transformations <10ms (likely corrupted)
Target: <3% outlier rate
FeedbackStorage
Dual-location JSON Lines storage:
import { FeedbackStorage } from '@angular-modernizer/core';
const storage = new FeedbackStorage({
projectRoot: '/path/to/project',
platformRoot: '/path/to/tsanalyzer',
storageLocation: 'both',
});
await storage.initSession(sessionId, session);
await storage.appendTransformation(sessionId, feedback);
const loadedSession = await storage.loadSession(sessionId);Storage paths:
- Project:
{projectRoot}/.claude-feedback/sessions/{sessionId}.jsonl - Platform:
{platformRoot}/.claude-feedback/sessions/{sessionId}.jsonl
The MCP feedback tools (collect-feedback, review-feedback, suggest-improvements, analyze-parser-performance) always use the project and add a platform root only when ANGULAR_MODERNIZER_PLATFORM_ROOT is set.
Format: JSON Lines (one JSON object per line, append-only)
Usage Patterns
Basic Kernel Setup
import { Kernel, RealFileSystemAdapter } from '@angular-modernizer/core';
import { StandalonePlugin } from '@angular-modernizer/plugin-standalone';
const kernel = new Kernel({
tsConfigPath: './tsconfig.json',
fileSystem: new RealFileSystemAdapter(),
plugins: [new StandalonePlugin()],
});
await kernel.initialize();
const project = kernel.getProject();
const plugin = kernel.getPlugin('@angular-modernizer/plugin-standalone');Testing with In-Memory File System
import { Kernel, InMemoryFileSystemAdapter } from '@angular-modernizer/core';
describe('MyPlugin', () => {
let kernel: Kernel;
beforeEach(async () => {
kernel = new Kernel({
fileSystem: new InMemoryFileSystemAdapter(),
projectOptions: {
useInMemoryFileSystem: true,
compilerOptions: {
target: 99, // ES2022
module: 199, // NodeNext
},
},
plugins: [new MyPlugin()],
});
await kernel.initialize();
});
it('should work with in-memory files', () => {
const project = kernel.getProject();
const sourceFile = project.createSourceFile(
'test.component.ts',
'@Component({}) export class TestComponent {}',
);
expect(sourceFile).toBeDefined();
});
});Plugin Development
import { Plugin, Kernel } from '@angular-modernizer/core';
class MyCustomPlugin implements Plugin {
readonly name = '@my-org/plugin-custom';
readonly version = '1.0.0';
readonly description = 'Custom analysis and transformation rules';
async initialize(kernel: Kernel): Promise<void> {
console.log('Custom plugin initialized');
}
getAnalysisRules(): AnalysisRule[] {
return [new MyAnalysisRule()];
}
getTransformRules(): TransformRule[] {
return [new MyTransformRule()];
}
getValidationRules(): ValidationRule[] {
return [];
}
}
const kernel = new Kernel({
plugins: [new MyCustomPlugin()],
});Intelligent Mode Selection
The orchestration system automatically chooses between Promise-based and worker thread parallelism based on project complexity:
import { ParallelExecutor } from '@angular-modernizer/core';
const executor = new ParallelExecutor({ maxWorkers: 4 });
const results = await executor.executeParallel(sourceFiles, rules, {
autoSelectMode: true,
});
// Mode selection analyzes:
// - File count (30% weight)
// - Total LOC/size (35% weight)
// - Large file outliers (20% weight)
// - Current memory pressure (15% weight)
//
// Decision thresholds:
// - Score < 35: Promise mode (safe, fast startup)
// - Score 35-50: Promise mode with monitoring and fallback to workers
// - Score 50-75: Worker threads preferred
// - Score >= 75: Worker threads requiredPromise mode monitoring and fallback:
const results = await executor.executeParallel(sourceFiles, rules, {
autoSelectMode: true,
enableMonitoring: true,
fallbackToWorkers: true,
workerScriptPath: getWorkerScriptPath(),
tsConfigPath: './tsconfig.json',
});
// If Promise mode hits 85% heap limit:
// -> Aborts execution
// -> Automatically switches to worker threads
// -> Completes analysis safelyForce override (always respected):
const results = await executor.executeParallel(sourceFiles, rules, {
useWorkerThreads: false, // Force Promise mode
});The thresholds option exists in ParallelOptions but the mode selector currently always uses its default thresholds.
Advanced Orchestration
import { Kernel, RealFileSystemAdapter } from '@angular-modernizer/core';
import { SolidPlugin } from '@angular-modernizer/plugin-solid';
import { getWorkerScriptPath } from '@angular-modernizer/worker';
const kernel = new Kernel({
tsConfigPath: './tsconfig.json',
fileSystem: new RealFileSystemAdapter(),
plugins: [new SolidPlugin()],
resourceLimits: { maxWorkers: 4, maxMemory: 512 * 1024 * 1024 },
});
await kernel.initialize();
// Promise mode with progress
const results = await kernel.analyzeParallel({
useWorkerThreads: false,
onProgress: (progress) => {
console.log(`Progress: ${progress.percentage}% (${progress.message})`);
},
});
// Worker threads
const workerResults = await kernel.analyzeParallel({
useWorkerThreads: true,
tsConfigPath: './tsconfig.json',
workerScriptPath: getWorkerScriptPath(),
});
// Incremental
const incrementalResult = await kernel.analyzeIncremental({
baseBranch: 'main',
includeDependents: true,
});Code Transformations
import { Kernel, RealFileSystemAdapter } from '@angular-modernizer/core';
import { StandalonePlugin } from '@angular-modernizer/plugin-standalone';
const kernel = new Kernel({
tsConfigPath: './tsconfig.json',
fileSystem: new RealFileSystemAdapter(),
plugins: [new StandalonePlugin()],
});
await kernel.initialize();
const sourceFile = kernel.getProject().getSourceFile('component.ts');
const result = await kernel.transformFile(sourceFile, {
dryRun: false,
autoSave: true,
pluginConfig: {
'@angular-modernizer/plugin-standalone': {
migrationType: 'to-standalone',
},
},
});
console.log(`Success: ${result.success}`);
console.log(`Rules executed: ${result.ruleResults.length}`);
console.log(`Errors: ${result.errors.length}`);Design Principles
Domain Agnostic
The core package contains no Angular-specific logic:
// Correct: generic infrastructure
class Kernel {
// Manages any TypeScript project
// Works with any plugins
// Uses any file system
}Optional Configuration
All configuration is optional with sensible defaults:
new Kernel();
new Kernel({});
new Kernel({ tsConfigPath: './tsconfig.json' });
new Kernel({ plugins: [plugin] });Async by Default
All I/O operations are async for scalability:
await fileSystem.readFile('file.ts');
await fileSystem.writeFile('file.ts', content);
await kernel.initialize();
await plugin.initialize(kernel);Testability First
In-memory file system enables fast, isolated testing:
const kernel = new Kernel({
fileSystem: new InMemoryFileSystemAdapter(),
});Package Structure
packages/core/
├── src/
│ ├── kernel.ts
│ ├── kernel-factory.ts # createKernel, findTsConfigPath
│ ├── file-system.ts # FileSystemAdapter, Real/InMemory adapters
│ ├── index.ts
│ ├── analysis/ # dependency graph, path aliases, false positives
│ ├── cache/ # CacheManager, MultiCacheFallbackManager
│ ├── config/ # ConfigLoader, config types
│ ├── feedback/ # FeedbackCollector, PrivacyFilter, storage
│ ├── fluent/ # FluentKernel pipeline
│ ├── orchestration/
│ │ ├── parallel-executor.ts
│ │ ├── mode-selector.ts
│ │ ├── incremental-analyzer.ts
│ │ ├── progress-reporter.ts
│ │ ├── promise-monitor.ts
│ │ ├── resource-manager.ts
│ │ ├── worker-pool.ts
│ │ ├── types.ts
│ │ └── index.ts
│ ├── parsers/ # parser strategies, external decorators
│ ├── pattern-analysis/
│ ├── rollback/ # BackupManager, GitIntegration, RollbackManager
│ ├── streaming/ # StreamingKernelWrapper, diffs
│ ├── testing/ # RealFsTestHelper
│ ├── transformation/
│ │ ├── transformation-coordinator.ts
│ │ ├── transform-execution-engine.ts
│ │ ├── transform-rule-executor.ts
│ │ ├── transformation-result-validator.ts
│ │ └── index.ts
│ ├── utils/
│ └── validation/ # build, TypeScript and pre/post-transform validation
├── __tests__/
├── schemas/ # angular-modernizer.schema.json
├── CACHE.md
├── package.json
├── tsconfig.json
├── jest.config.js
└── README.mdDependencies
- ts-morph: TypeScript AST manipulation and project management
- @angular-modernizer/api, @angular-modernizer/plugin-system: public API and plugin contracts
- ajv: JSON Schema validation of
.angular-modernizer.json - diff: unified diffs for streaming results
- rxjs: optional peer dependency for the RxJS integration of the fluent API
Testing
pnpm --filter @angular-modernizer/core test
pnpm --filter @angular-modernizer/core test --coverage
pnpm --filter @angular-modernizer/core benchTest categories:
- Kernel: initialization, configuration, plugin management
- File System: both real and in-memory implementations
- Cache: invalidation, LRU eviction, file change detection
- Transformation: coordinator, execution engine, context management
- Orchestration: parallel execution, incremental analysis, progress reporting, resource management, worker threads
- Performance benchmarks: cache performance, orchestration scaling, memory usage
- Integration: end-to-end kernel usage scenarios with real plugins
- Error handling: edge cases and failure scenarios
Worker thread tests run only with ENABLE_WORKER_TESTS=true.
Testing patterns:
describe('Kernel', () => {
describe('initialization', () => {
it('should initialize with minimal config', async () => {
const kernel = new Kernel();
await expect(kernel.initialize()).resolves.not.toThrow();
expect(kernel.isInitialized()).toBe(true);
});
it('should initialize with full config', async () => {
const kernel = new Kernel({
tsConfigPath: './tsconfig.json',
fileSystem: new RealFileSystemAdapter(),
plugins: [new TestPlugin()],
});
await kernel.initialize();
expect(kernel.getAllPlugins()).toHaveLength(1);
});
});
});Performance Considerations
Memory Management
- ts-morph projects can be memory-intensive for large codebases
- Use
InMemoryFileSystemAdapterfor testing to reduce memory footprint - Consider incremental analysis for very large projects
File System Operations
RealFileSystemAdapteruses async operations for scalability- Batch file operations when possible
Plugin Initialization
- Plugins are initialized sequentially to avoid race conditions
- Plugin initialization is async to support I/O operations
- A failing plugin
initialize()abortskernel.initialize()withFailed to initialize plugin "<name>"; plugin names must be unique
Troubleshooting
Common Issues
Kernel initialization fails:
const kernel = new Kernel({
tsConfigPath: './tsconfig.json', // Ensure this file exists
});Plugin not found:
const kernel = new Kernel({
plugins: [new MyPlugin()],
});
await kernel.initialize();
console.log(kernel.getAllPlugins());
console.log(kernel.hasPlugin('my-plugin-name'));File system errors:
const fileSystem = new RealFileSystemAdapter();
try {
await fileSystem.exists('./src');
} catch (error) {
console.error('File system error:', error);
}ESLint / TypeScript IDE Errors
If VSCode shows @typescript-eslint/no-unsafe-assignment errors but the build passes:
- Verify the code compiles:
pnpm --filter @angular-modernizer/core build - Verify ESLint passes:
npx eslint packages/core/src/orchestration/promise-monitor.ts - If both pass, reload the TypeScript server in VSCode (Cmd+Shift+P > "TypeScript: Restart TS Server")
This is a known issue with TypeScript language server type inference in certain contexts involving complex type narrowing. The satisfies operator can help:
private collectMetrics(): RuntimeMetrics {
return {
// ... properties
} satisfies RuntimeMetrics;
}Memory Issues with Promise-Based Parallelism
If you see FATAL ERROR: Reached heap limit Allocation failed with 500+ files:
const results = await kernel.analyzeParallel({
useWorkerThreads: true,
tsConfigPath: './tsconfig.json',
workerScriptPath: getWorkerScriptPath(),
maxWorkers: 4,
minFilesForWorkers: 200,
});Worker threads use isolated heaps and avoid the V8 heap fragmentation that causes crashes in Promise-based parallelism.
Worker Thread Build Errors
If worker threads fail with "Cannot find module" errors, build all packages:
pnpm run build
# Or build worker specifically
pnpm --filter @angular-modernizer/worker buildPromise-based parallelism does not require building and works immediately.
Streaming and Structured Diffs
The streaming path (deprecated on the MCP side, where tools use native progress and cancellation) is exported from the package root:
import {
StreamingKernelWrapper,
generateUnifiedDiff,
generateDiffSummary,
detectSyntaxErrors,
type FileDiff,
type DiffHunk,
type LineChange,
type SemanticAnnotation,
type AngularContext,
type SyntaxType,
type StreamingOptions,
type StreamingProgressEvent,
type StreamingTransformResult,
} from '@angular-modernizer/core';See Also
- packages/api/README.md - Public API tools and utilities
- packages/plugin-system/README.md - Plugin contracts and interfaces
- Orchestration source (
src/orchestration/) - Orchestration and parallel processing - CACHE.md - Comprehensive cache documentation
License
MIT License - See LICENSE file for details
