Function bodies 337 total
GitHubUrlHandler.cloneRepository method · javascript · L110-L153 (44 LOC)src/services/GitHubUrlHandler.js
async cloneRepository() {
// Detect default branch if needed
if (!this.branch) {
this.branch = await this.detectDefaultBranch();
this.updateCacheKey();
this.repoDir = path.join(this.cacheDir, this.cacheKey);
}
// Clean up existing directory if it exists
if (fs.existsSync(this.repoDir)) {
logger.warn('Repository cache already exists, removing...', {
cacheDir: this.repoDir,
});
fs.rmSync(this.repoDir, { recursive: true, force: true });
}
const command = `git clone --branch ${this.branch} --single-branch ${this.repoUrl} ${this.repoDir}`;
try {
logger.info('Cloning repository', {
repo: this.repoUrl,
branch: this.branch,
});
execSync(command, { stdio: 'pipe' });
logger.info('Repository cloned successfully', {
repo: this.repoUrl,
cacheDir: this.repoDir,
});
} catch (error) {
// Handle specific errors
if (error.message.includes('AutheGitHubUrlHandler.updateRepository method · javascript · L158-L199 (42 LOC)src/services/GitHubUrlHandler.js
async updateRepository() {
try {
logger.info('Updating repository', {
repo: this.repoUrl,
cacheDir: this.repoDir,
});
// Fetch latest changes
execSync('git fetch', { cwd: this.repoDir, stdio: 'pipe' });
// Check if behind origin
const behindCount = parseInt(
execSync(`git rev-list HEAD..origin/${this.branch} --count`, {
cwd: this.repoDir,
encoding: 'utf8',
}).trim(),
);
if (behindCount > 0) {
logger.info(`Repository is ${behindCount} commits behind, updating...`);
// Reset and clean to avoid conflicts
execSync('git reset --hard HEAD', { cwd: this.repoDir, stdio: 'pipe' });
execSync('git clean -fd', { cwd: this.repoDir, stdio: 'pipe' });
// Pull latest changes
execSync(`git pull origin ${this.branch}`, { cwd: this.repoDir, stdio: 'pipe' });
logger.info('Repository updated successfully');
} else {
logger.infoGitHubUrlHandler.detectDefaultBranch method · javascript · L204-L222 (19 LOC)src/services/GitHubUrlHandler.js
async detectDefaultBranch() {
try {
const output = execSync(`git ls-remote --symref ${this.repoUrl} HEAD`, {
encoding: 'utf8',
stdio: 'pipe',
});
const match = output.match(/ref: refs\/heads\/([^\s]+)\s+HEAD/);
const branch = match ? match[1] : 'main';
logger.info('Detected default branch', { branch });
return branch;
} catch (error) {
logger.warn('Failed to detect default branch, using "main"', {
error: error.message,
});
return 'main';
}
}GitHubUrlHandler.getCacheInfo method · javascript · L227-L236 (10 LOC)src/services/GitHubUrlHandler.js
getCacheInfo() {
return {
cacheKey: this.cacheKey,
cacheDir: this.repoDir,
exists: fs.existsSync(this.repoDir),
repository: this.repoUrl,
branch: this.branch,
subPath: this.subPath,
};
}GitHubUrlHandler.clearCache method · javascript · L241-L246 (6 LOC)src/services/GitHubUrlHandler.js
async clearCache() {
if (fs.existsSync(this.repoDir)) {
logger.info('Clearing repository cache', { cacheDir: this.repoDir });
fs.removeSync(this.repoDir);
}
}GitHubUrlHandler.getStats method · javascript · L251-L279 (29 LOC)src/services/GitHubUrlHandler.js
async getStats() {
if (!fs.existsSync(this.repoDir)) {
return null;
}
try {
const stats = {
lastFetch: fs.statSync(path.join(this.repoDir, '.git', 'FETCH_HEAD')).mtime,
size: await this.getDirectorySize(this.repoDir),
commitCount: parseInt(
execSync('git rev-list --count HEAD', {
cwd: this.repoDir,
encoding: 'utf8',
}).trim(),
),
currentCommit: execSync('git rev-parse HEAD', {
cwd: this.repoDir,
encoding: 'utf8',
})
.trim()
.substring(0, 7),
};
return stats;
} catch (error) {
logger.warn('Failed to get repository stats', { error: error.message });
return null;
}
}GitHubUrlHandler.execSync method · javascript · L261-L269 (9 LOC)src/services/GitHubUrlHandler.js
execSync('git rev-list --count HEAD', {
cwd: this.repoDir,
encoding: 'utf8',
}).trim(),
),
currentCommit: execSync('git rev-parse HEAD', {
cwd: this.repoDir,
encoding: 'utf8',
})Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
GitHubUrlHandler.getDirectorySize method · javascript · L284-L300 (17 LOC)src/services/GitHubUrlHandler.js
async getDirectorySize(dirPath) {
let size = 0;
const files = await fs.readdir(dirPath);
for (const file of files) {
const filePath = path.join(dirPath, file);
const stat = await fs.stat(filePath);
if (stat.isDirectory() && file !== '.git') {
size += await this.getDirectorySize(filePath);
} else if (stat.isFile()) {
size += stat.size;
}
}
return size;
}GitleaksAdapter.constructor method · javascript · L27-L33 (7 LOC)src/services/GitleaksAdapter.js
constructor(options = {}) {
this.binaryPath = options.binaryPath || 'gitleaks';
this.configPath = options.configPath || null;
this.extraArgs = options.extraArgs || [];
this.logLevel = options.logLevel || 'fatal';
this._available = null; // Cache availability check
}GitleaksAdapter.isAvailable method · javascript · L39-L52 (14 LOC)src/services/GitleaksAdapter.js
async isAvailable() {
if (this._available !== null) {
return this._available;
}
try {
await execAsync(`"${this.binaryPath}" version`, { timeout: 5000 });
this._available = true;
return true;
} catch (error) {
this._available = false;
return false;
}
}GitleaksAdapter.scanString method · javascript · L62-L135 (74 LOC)src/services/GitleaksAdapter.js
async scanString(content, logicalPath = 'stdin') {
// Check gitleaks version for compatibility
const version = (await this.getVersion()) || '';
const supportsStdin = /^8\.(19|[2-9]\d)\./.test(version) || /^9\./.test(version);
const supportsRedactPercent = /^8\.(19|[2-9]\d)\./.test(version) || /^9\./.test(version);
// Build command arguments with version-appropriate options
const args = [];
// Use stdin if available, otherwise use detect
if (supportsStdin) {
args.push('stdin');
} else {
args.push('detect', '--no-git');
}
args.push(
'--report-format',
'json',
'--report-path',
'-', // stdout
'--no-banner',
'--no-color',
'--log-level',
this.logLevel,
);
// Add redaction flag based on version
if (supportsRedactPercent) {
args.push('--redact=100'); // Newer versions support percentage
} else {
args.push('--redact'); // Older versions use boolean flag
}GitleaksAdapter._executeGitleaks method · javascript · L144-L212 (69 LOC)src/services/GitleaksAdapter.js
async _executeGitleaks(args, stdin) {
return new Promise((resolve, reject) => {
const child = spawn(this.binaryPath, args, {
stdio: ['pipe', 'pipe', 'pipe'],
});
let stdout = '';
let stderr = '';
let timedOut = false;
// Set timeout to kill hung scans
const timeoutMs = 10000; // 10 seconds
const timer = setTimeout(() => {
timedOut = true;
try {
child.kill();
} catch (e) {
// Process may have already exited
}
}, timeoutMs);
child.stdout.on('data', (data) => {
stdout += data.toString();
});
child.stderr.on('data', (data) => {
stderr += data.toString();
});
child.on('error', (error) => {
clearTimeout(timer);
reject(new Error(`Failed to spawn gitleaks: ${error.message}`));
});
child.on('close', (code) => {
clearTimeout(timer);
if (timedOut) {
reject(new Error('GitleaGitleaksAdapter.getVersion method · javascript · L218-L227 (10 LOC)src/services/GitleaksAdapter.js
async getVersion() {
try {
const { stdout } = await execAsync(`"${this.binaryPath}" version`, { timeout: 5000 });
// Parse version from output (format: "v8.19.0" or similar)
const match = stdout.match(/v?(\d+\.\d+\.\d+)/);
return match ? match[1] : stdout.trim();
} catch (error) {
return null;
}
}InstructionsLoader.constructor method · javascript · L30-L40 (11 LOC)src/services/InstructionsLoader.js
constructor(basePath = process.cwd()) {
this.basePath = basePath;
this.logger = logger?.child ? logger.child('InstructionsLoader') : logger;
// Instructions directories (in order of priority)
this.userDir = path.join(os.homedir(), '.copytree/instructions');
this.appDir = path.join(moduleDir, '..', 'templates', 'instructions');
// Cache for loaded instructions
this.instructionsCache = new Map();
}InstructionsLoader.load method · javascript · L47-L94 (48 LOC)src/services/InstructionsLoader.js
async load(name = 'default') {
try {
// Check cache first
const cacheKey = name;
if (this.instructionsCache.has(cacheKey)) {
return this.instructionsCache.get(cacheKey);
}
// Search for instructions file
const userPath = path.join(this.userDir, `${name}.md`);
const appPath = path.join(this.appDir, `${name}.md`);
let content;
let foundPath;
// Check user directory first (highest priority)
if (await fs.pathExists(userPath)) {
content = await fs.readFile(userPath, 'utf8');
foundPath = userPath;
if (this.logger?.debug) {
this.logger.debug(`Loading instructions from user directory: ${userPath}`);
}
}
// Then check app directory (built-in)
else if (await fs.pathExists(appPath)) {
content = await fs.readFile(appPath, 'utf8');
foundPath = appPath;
} else {
const { InstructionsError, CopyTreeError } = await import('../utils/Same scanner, your repo: https://repobility.com — Repobility
InstructionsLoader.list method · javascript · L100-L128 (29 LOC)src/services/InstructionsLoader.js
async list() {
const instructionsSet = new Set();
// Helper to add instructions from a directory
const addInstructionsFromDir = async (dir) => {
if (!(await fs.pathExists(dir))) return;
try {
const files = await fs.readdir(dir);
for (const file of files) {
const ext = path.extname(file).toLowerCase();
if (ext === '.md') {
const name = path.basename(file, path.extname(file)).toLowerCase();
instructionsSet.add(name);
}
}
} catch (error) {
if (this.logger?.warn) {
this.logger.warn(`Failed to read instructions directory ${dir}: ${error.message}`);
}
}
};
// Scan both directories
await addInstructionsFromDir(this.userDir);
await addInstructionsFromDir(this.appDir);
return Array.from(instructionsSet).sort();
}InstructionsLoader.exists method · javascript · L135-L140 (6 LOC)src/services/InstructionsLoader.js
async exists(name = 'default') {
const userPath = path.join(this.userDir, `${name}.md`);
const appPath = path.join(this.appDir, `${name}.md`);
return (await fs.pathExists(userPath)) || (await fs.pathExists(appPath));
}InstructionsLoader.clearCache method · javascript · L145-L150 (6 LOC)src/services/InstructionsLoader.js
clearCache() {
this.instructionsCache.clear();
if (this.logger?.debug) {
this.logger.debug('Instructions cache cleared');
}
}InstructionsLoader.getCacheStats method · javascript · L156-L161 (6 LOC)src/services/InstructionsLoader.js
getCacheStats() {
return {
size: this.instructionsCache.size,
keys: Array.from(this.instructionsCache.keys()),
};
}BaseTransformer.constructor method · javascript · L12-L30 (19 LOC)src/transforms/BaseTransformer.js
constructor(options = {}) {
this.options = options;
this.config = config();
this.logger = options.logger || logger.child(this.constructor.name);
// Enable caching based on configuration
// Default to false for non-AI transformers; AI transformers should override
this.cacheEnabled = options.noCache
? false
: (options.cache ?? this.config.get('cache.transformations.enabled', false));
this.cacheTTL = options.cacheTTL ?? this.config.get('cache.transformations.ttl', 86400);
// Indicates whether this transformer performs heavy/slow operations
// Heavy transformers will show progress during processing
this.isHeavy = false;
// Cache will be initialized on first use if caching is enabled
this.cache = null;
}BaseTransformer.initializeCache method · javascript · L35-L45 (11 LOC)src/transforms/BaseTransformer.js
initializeCache() {
if (!this.cache && this.cacheEnabled) {
const transformerName = this.constructor.name.toLowerCase().replace('transformer', '');
const specificConfig = `cache.transformations.${transformerName}`;
this.cache = CacheService.create('transform', {
enabled: this.config.get(`${specificConfig}.enabled`, this.cacheEnabled),
defaultTtl: this.config.get(`${specificConfig}.ttl`, this.cacheTTL),
});
}
}BaseTransformer.transform method · javascript · L52-L98 (47 LOC)src/transforms/BaseTransformer.js
async transform(file) {
try {
// Validate input first
this.validateInput(file);
this.logger.debug(`Transforming ${file.path}`);
const startTime = Date.now();
// Initialize cache if needed
this.initializeCache();
// Check cache if enabled
if (this.cacheEnabled && this.cache) {
const cached = await this.getFromCache(file);
if (cached) {
this.logger.debug(`Cache hit for ${file.path}`);
return cached;
}
}
// Perform transformation
const result = await this.doTransform(file);
// Validate output
this.validateOutput(result);
// Cache result if enabled and cache exists
if (this.cacheEnabled && this.cache) {
await this.saveToCache(file, result);
}
const duration = Date.now() - startTime;
this.logger.debug(`Transformed ${file.path} in ${duration}ms`);
return result;
} catch (error) {
if (error instanceof TranBaseTransformer.validateInput method · javascript · L115-L122 (8 LOC)src/transforms/BaseTransformer.js
validateInput(file) {
if (!file) {
throw new TransformError('File object is required', this.constructor.name);
}
if (!file.path) {
throw new TransformError('File path is required', this.constructor.name);
}
}About: code-quality intelligence by Repobility · https://repobility.com
BaseTransformer.getCacheKey method · javascript · L140-L150 (11 LOC)src/transforms/BaseTransformer.js
getCacheKey(file) {
const parts = [
this.constructor.name,
file.path,
file.size || 0,
file.modified ? file.modified.getTime() : 0,
JSON.stringify(this.options),
];
return crypto.createHash('sha256').update(parts.join(':')).digest('hex');
}BaseTransformer.getFromCache method · javascript · L157-L173 (17 LOC)src/transforms/BaseTransformer.js
async getFromCache(file) {
if (!this.cache) return null;
const cacheKey = this.getCacheKey(file);
const cached = await this.cache.get(cacheKey);
if (cached) {
// Merge cached content back into file object
return {
...file,
...cached,
fromCache: true,
};
}
return null;
}BaseTransformer.saveToCache method · javascript · L181-L199 (19 LOC)src/transforms/BaseTransformer.js
async saveToCache(file, result) {
if (!this.cache) return;
const cacheKey = this.getCacheKey(file);
// Only cache successful transformations
if (result.transformed) {
// Don't cache the entire file object, just the transformation results
const cacheData = {
content: result.content,
transformed: result.transformed,
transformedBy: result.transformedBy,
metadata: result.metadata,
error: result.error,
};
await this.cache.set(cacheKey, cacheData, this.cacheTTL);
}
}BaseTransformer.getMetadata method · javascript · L214-L221 (8 LOC)src/transforms/BaseTransformer.js
getMetadata() {
return {
name: this.constructor.name,
description: this.description || 'No description available',
supportedExtensions: this.supportedExtensions || [],
options: this.options,
};
}BaseTransformer.isTextContent method · javascript · L228-L241 (14 LOC)src/transforms/BaseTransformer.js
isTextContent(content) {
if (typeof content === 'string') return true;
if (Buffer.isBuffer(content)) {
// Check first 512 bytes for null characters
const sample = content.slice(0, Math.min(512, content.length));
for (let i = 0; i < sample.length; i++) {
if (sample[i] === 0) return false;
}
return true;
}
return false;
}BaseTransformer.detectEncoding method · javascript · L248-L263 (16 LOC)src/transforms/BaseTransformer.js
detectEncoding(buffer) {
// Simple BOM detection
if (buffer.length >= 3) {
if (buffer[0] === 0xef && buffer[1] === 0xbb && buffer[2] === 0xbf) {
return 'utf8';
}
if (buffer[0] === 0xff && buffer[1] === 0xfe) {
return 'utf16le';
}
if (buffer[0] === 0xfe && buffer[1] === 0xff) {
return 'utf16be';
}
}
return 'utf8'; // Default
}BaseTransformer.formatBytes method · javascript · L270-L276 (7 LOC)src/transforms/BaseTransformer.js
formatBytes(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}TransformerRegistry.constructor method · javascript · L15-L28 (14 LOC)src/transforms/TransformerRegistry.js
constructor(options = {}) {
this.transformers = new Map();
this.extensionMap = new Map();
this.mimeTypeMap = new Map();
this.defaultTransformer = null;
this.logger = logger.child('TransformerRegistry');
// Traits system
this.traits = new Map(); // transformer name -> traits
this.validationEnabled = true;
// Store config for potential use by transformers
this.config = options.config || null;
}If a scraper extracted this row, it came from Repobility (https://repobility.com)
TransformerRegistry.register method · javascript · L37-L81 (45 LOC)src/transforms/TransformerRegistry.js
register(name, transformer, options = {}, traits = null) {
if (this.transformers.has(name)) {
this.logger.warn(`Overwriting existing transformer: ${name}`);
}
this.transformers.set(name, {
transformer,
options,
priority: options.priority || 0,
});
// Register traits if provided
if (traits) {
this.traits.set(name, this._normalizeTraits(traits));
this.logger.debug(`Registered traits for transformer: ${name}`, traits);
}
// Register extensions
if (options.extensions) {
options.extensions.forEach((ext) => {
const normalizedExt = ext.startsWith('.') ? ext : `.${ext}`;
if (!this.extensionMap.has(normalizedExt)) {
this.extensionMap.set(normalizedExt, []);
}
this.extensionMap.get(normalizedExt).push(name);
});
}
// Register MIME types
if (options.mimeTypes) {
options.mimeTypes.forEach((mimeType) => {
if (!this.mimeTypeMap.has(mimeType)) {TransformerRegistry.get method · javascript · L88-L94 (7 LOC)src/transforms/TransformerRegistry.js
get(name) {
const entry = this.transformers.get(name);
if (!entry) {
throw new TransformError(`Transformer not found: ${name}`, name);
}
return entry.transformer;
}TransformerRegistry.getForFile method · javascript · L101-L131 (31 LOC)src/transforms/TransformerRegistry.js
getForFile(file) {
const transformerNames = [];
// Check by extension
const ext = this.getExtension(file.path);
if (ext && this.extensionMap.has(ext)) {
transformerNames.push(...this.extensionMap.get(ext));
}
// Check by MIME type
if (file.mimeType && this.mimeTypeMap.has(file.mimeType)) {
transformerNames.push(...this.mimeTypeMap.get(file.mimeType));
}
// Get unique transformer names with highest priority
const uniqueNames = [...new Set(transformerNames)];
if (uniqueNames.length > 0) {
const sorted = uniqueNames
.map((name) => ({ name, ...this.transformers.get(name) }))
.sort((a, b) => b.priority - a.priority);
return this.get(sorted[0].name);
}
// Return default transformer
if (this.defaultTransformer) {
return this.get(this.defaultTransformer);
}
throw new TransformError(`No transformer found for file: ${file.path}`, 'unknown', file.path);
}TransformerRegistry.list method · javascript · L146-L159 (14 LOC)src/transforms/TransformerRegistry.js
list() {
return Array.from(this.transformers.entries()).map(([name, entry]) => ({
name,
priority: entry.priority,
extensions: Array.from(this.extensionMap.entries())
.filter(([_, names]) => names.includes(name))
.map(([ext]) => ext),
mimeTypes: Array.from(this.mimeTypeMap.entries())
.filter(([_, names]) => names.includes(name))
.map(([mime]) => mime),
isDefault: this.defaultTransformer === name,
traits: this.traits.get(name) || null,
}));
}TransformerRegistry.clear method · javascript · L172-L178 (7 LOC)src/transforms/TransformerRegistry.js
clear() {
this.transformers.clear();
this.extensionMap.clear();
this.mimeTypeMap.clear();
this.traits.clear();
this.defaultTransformer = null;
}TransformerRegistry.validateDependencies method · javascript · L185-L240 (56 LOC)src/transforms/TransformerRegistry.js
validateDependencies() {
const VISITING = 1;
const DONE = 2;
const state = new Map();
const order = [];
const getDeps = (name) => {
const traits = this.traits.get(name);
if (!traits || !traits.dependencies) {
return [];
}
return Array.isArray(traits.dependencies) ? traits.dependencies : [];
};
const visit = (name, stack = []) => {
const mark = state.get(name) || 0;
if (mark === VISITING) {
const cycle = [...stack, name].join(' -> ');
throw new TransformError(`Circular dependency detected: ${cycle}`, 'CIRCULAR_DEPENDENCY');
}
if (mark === DONE) {
return;
}
// Check if transformer exists
if (!this.transformers.has(name)) {
throw new TransformError(`Missing transformer dependency: ${name}`, 'MISSING_DEPENDENCY');
}
state.set(name, VISITING);
stack.push(name);
for (const dep of getDeps(name)) {
// Dependencies can be TransformerRegistry.validatePlan method · javascript · L247-L296 (50 LOC)src/transforms/TransformerRegistry.js
validatePlan(stages) {
if (!this.validationEnabled || !stages || stages.length === 0) {
return { valid: true, issues: [], warnings: [] };
}
const issues = [];
const warnings = [];
// First, validate dependencies for circular references
try {
this.validateDependencies();
} catch (error) {
if (error.code === 'CIRCULAR_DEPENDENCY' || error.code === 'MISSING_DEPENDENCY') {
issues.push({
type: 'dependency_error',
severity: 'error',
message: error.message,
transformers: stages,
});
return { valid: false, issues, warnings };
}
throw error;
}
// Check for conflicts between transformers
for (let i = 0; i < stages.length; i++) {
for (let j = i + 1; j < stages.length; j++) {
const conflicts = this._checkConflicts(stages[i], stages[j]);
issues.push(...conflicts);
}
}
// Check ordering issues
const orderingIssues = this._TransformerRegistry.optimizePlan method · javascript · L303-L359 (57 LOC)src/transforms/TransformerRegistry.js
optimizePlan(stages) {
if (!stages || stages.length <= 1) {
return {
optimized: stages || [],
changes: [],
reasoning: [],
};
}
const optimized = [...stages];
const changes = [];
const reasoning = [];
// Sort by dependency requirements (order-sensitive transformers first)
const withTraits = optimized.map((name) => ({
name,
traits: this.traits.get(name) || {},
}));
// Move order-sensitive transformers to appropriate positions
const orderSensitive = withTraits.filter((t) => t.traits.orderSensitive);
const orderInsensitive = withTraits.filter((t) => !t.traits.orderSensitive);
const heavy = withTraits.filter((t) => t.traits.heavy);
// Rebuild order: order-sensitive first, then light operations, then heavy operations
const reordered = [
...orderSensitive.filter((t) => !t.traits.heavy),
...orderInsensitive.filter((t) => !t.traits.heavy),
...heavy,
];
const oGenerated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
TransformerRegistry._normalizeTraits method · javascript · L382-L414 (33 LOC)src/transforms/TransformerRegistry.js
_normalizeTraits(traits) {
const normalized = {
inputTypes: traits.inputTypes || ['text'],
outputTypes: traits.outputTypes || ['text'],
idempotent: traits.idempotent ?? true,
orderSensitive: traits.orderSensitive ?? false,
dependencies: traits.dependencies || [],
heavy: traits.heavy ?? false,
stateful: traits.stateful ?? false,
conflictsWith: traits.conflictsWith || [],
requirements: traits.requirements || {},
tags: traits.tags || [],
};
// Validate trait values
if (!Array.isArray(normalized.inputTypes)) {
normalized.inputTypes = [normalized.inputTypes];
}
if (!Array.isArray(normalized.outputTypes)) {
normalized.outputTypes = [normalized.outputTypes];
}
if (!Array.isArray(normalized.dependencies)) {
normalized.dependencies = [normalized.dependencies];
}
if (!Array.isArray(normalized.conflictsWith)) {
normalized.conflictsWith = [normalized.conflictsWith];
}
TransformerRegistry._checkConflicts method · javascript · L420-L467 (48 LOC)src/transforms/TransformerRegistry.js
_checkConflicts(transformer1, transformer2) {
const issues = [];
const traits1 = this.traits.get(transformer1);
const traits2 = this.traits.get(transformer2);
if (!traits1 || !traits2) {
return issues; // Skip validation if traits not available
}
// Check explicit conflicts
if (traits1.conflictsWith.includes(transformer2)) {
issues.push({
type: 'conflict',
severity: 'error',
message: `Transformer '${transformer1}' conflicts with '${transformer2}'`,
transformers: [transformer1, transformer2],
});
}
if (traits2.conflictsWith.includes(transformer1)) {
issues.push({
type: 'conflict',
severity: 'error',
message: `Transformer '${transformer2}' conflicts with '${transformer1}'`,
transformers: [transformer1, transformer2],
});
}
// Check input/output type compatibility
const hasCompatibleTypes = traits1.outputTypes.some((output) =>
traits2.inTransformerRegistry._validateOrdering method · javascript · L473-L515 (43 LOC)src/transforms/TransformerRegistry.js
_validateOrdering(stages) {
const issues = [];
for (let i = 0; i < stages.length; i++) {
const traits = this.traits.get(stages[i]);
if (!traits) continue;
// Check if order-sensitive transformer is placed appropriately
if (traits.orderSensitive) {
// Look for non-idempotent transformers before this one
for (let j = 0; j < i; j++) {
const prevTraits = this.traits.get(stages[j]);
if (prevTraits && !prevTraits.idempotent) {
issues.push({
type: 'ordering',
severity: 'warning',
message: `Order-sensitive transformer '${stages[i]}' follows non-idempotent transformer '${stages[j]}', which may cause unpredictable results`,
transformers: [stages[j], stages[i]],
});
}
}
}
// Check if heavy transformer is placed optimally
if (traits.heavy && i < stages.length - 2) {
const remainingHeavy = stages.slice(i TransformerRegistry._validateResources method · javascript · L521-L550 (30 LOC)src/transforms/TransformerRegistry.js
_validateResources(stages) {
const issues = [];
const requiredResources = new Set();
for (const stage of stages) {
const traits = this.traits.get(stage);
if (!traits) continue;
// Check for required dependencies
for (const dep of traits.dependencies) {
requiredResources.add(dep);
}
// Check specific requirements
if (traits.requirements.apiKey) {
issues.push({
type: 'missing_resource',
severity: 'error',
message: `Transformer '${stage}' requires an API key but none is configured`,
transformers: [stage],
});
}
if (traits.requirements.network) {
// Could add network connectivity checks here
}
}
return issues;
}TransformerRegistry._generateWarnings method · javascript · L556-L600 (45 LOC)src/transforms/TransformerRegistry.js
_generateWarnings(stages) {
const warnings = [];
// Check for too many heavy operations
const heavyCount = stages.filter((name) => {
const traits = this.traits.get(name);
return traits && traits.heavy;
}).length;
if (heavyCount > 3) {
warnings.push({
type: 'performance',
severity: 'warning',
message: `Pipeline contains ${heavyCount} heavy transformers, which may impact performance`,
suggestion: 'Consider reducing the number of AI or computationally intensive transformers',
});
}
// Check for redundant transformers
const duplicateTags = new Map();
stages.forEach((name) => {
const traits = this.traits.get(name);
if (traits && traits.tags) {
traits.tags.forEach((tag) => {
if (!duplicateTags.has(tag)) {
duplicateTags.set(tag, []);
}
duplicateTags.get(tag).push(name);
});
}
});
for (const [tag, transformers] of TransformerRegistry.createDefault method · javascript · L620-L712 (93 LOC)src/transforms/TransformerRegistry.js
static async createDefault(options = {}) {
const registry = new TransformerRegistry();
// Store config for potential use by transformers
registry.config = options.config || null;
// Register default transformers - using dynamic imports for better ESM compatibility
const { default: FileLoaderTransformer } =
await import('./transformers/FileLoaderTransformer.js');
const { default: StreamingFileLoaderTransformer } =
await import('./transformers/StreamingFileLoaderTransformer.js');
const { default: BinaryTransformer } = await import('./transformers/BinaryTransformer.js');
// File Loader - default transformer
registry.register(
'file-loader',
new FileLoaderTransformer(),
{
isDefault: true,
priority: 0,
},
{
inputTypes: ['any'],
outputTypes: ['text', 'binary'],
idempotent: true,
orderSensitive: false,
heavy: false,
stateful: false,
dependenBinaryTransformer.constructor method · javascript · L10-L58 (49 LOC)src/transforms/transformers/BinaryTransformer.js
constructor(options = {}) {
super(options);
this.description = 'Handles binary files with placeholder text or base64 encoding';
this.supportedExtensions = [
// Images
'.jpg',
'.jpeg',
'.png',
'.gif',
'.bmp',
'.webp',
'.ico',
'.svg',
// Documents
'.pdf',
'.doc',
'.docx',
'.xls',
'.xlsx',
'.ppt',
'.pptx',
// Archives
'.zip',
'.tar',
'.gz',
'.rar',
'.7z',
'.bz2',
// Executables
'.exe',
'.dll',
'.so',
'.dylib',
'.app',
// Media
'.mp3',
'.mp4',
'.avi',
'.mov',
'.wmv',
'.flv',
'.webm',
// Other
'.db',
'.sqlite',
'.bin',
'.dat',
];
}BinaryTransformer.doTransform method · javascript · L60-L83 (24 LOC)src/transforms/transformers/BinaryTransformer.js
async doTransform(file) {
// Hard-exclude all non-image binary files
const isImage = isImageExtension(file.path);
if (!isImage) {
// Non-image binary files are completely excluded (no output)
return null;
}
// For image files, use configured action
const action = this.config.get('copytree.binaryFileAction', 'placeholder');
switch (action) {
case 'skip':
return null;
case 'base64':
return await this.transformToBase64(file);
case 'placeholder':
default:
return this.transformToPlaceholder(file);
}
}Same scanner, your repo: https://repobility.com — Repobility
BinaryTransformer.transformToPlaceholder method · javascript · L90-L110 (21 LOC)src/transforms/transformers/BinaryTransformer.js
transformToPlaceholder(file) {
const placeholderText = this.config.get(
'copytree.binaryPlaceholderText',
'[Binary file not included]',
);
const fileInfo = [
placeholderText,
`Type: ${this.getFileType(file.path)}`,
`Size: ${this.formatBytes(file.size || 0)}`,
].join('\n');
return {
...file,
content: fileInfo,
transformed: true,
transformedBy: this.constructor.name,
isBinary: true,
binaryAction: 'placeholder',
};
}BinaryTransformer.transformToBase64 method · javascript · L117-L157 (41 LOC)src/transforms/transformers/BinaryTransformer.js
async transformToBase64(file) {
const fs = await import('fs-extra');
try {
// Check size limit for base64 encoding
const maxBase64Size = this.config.get('copytree.maxBase64Size', 1024 * 1024); // 1MB default
if (file.size > maxBase64Size) {
this.logger.warn(
`File ${file.path} too large for base64 encoding (${this.formatBytes(file.size)})`,
);
return this.transformToPlaceholder(file);
}
const buffer = await fs.readFile(file.absolutePath);
const base64 = buffer.toString('base64');
// Add metadata comment
const content = [
'[Binary file encoded as base64]',
`Type: ${this.getFileType(file.path)}`,
`Size: ${this.formatBytes(file.size || 0)}`,
'Encoding: base64',
'',
base64,
].join('\n');
return {
...file,
content,
transformed: true,
transformedBy: this.constructor.name,
isBinary: true,
biBinaryTransformer.getFileType method · javascript · L164-L208 (45 LOC)src/transforms/transformers/BinaryTransformer.js
getFileType(filePath) {
const ext = path.extname(filePath).toLowerCase();
const typeMap = {
// Images
'.jpg': 'JPEG Image',
'.jpeg': 'JPEG Image',
'.png': 'PNG Image',
'.gif': 'GIF Image',
'.bmp': 'Bitmap Image',
'.webp': 'WebP Image',
'.ico': 'Icon',
'.svg': 'SVG Image',
// Documents
'.pdf': 'PDF Document',
'.doc': 'Word Document',
'.docx': 'Word Document',
'.xls': 'Excel Spreadsheet',
'.xlsx': 'Excel Spreadsheet',
'.ppt': 'PowerPoint Presentation',
'.pptx': 'PowerPoint Presentation',
// Archives
'.zip': 'ZIP Archive',
'.tar': 'TAR Archive',
'.gz': 'Gzip Archive',
'.rar': 'RAR Archive',
'.7z': '7-Zip Archive',
// Media
'.mp3': 'MP3 Audio',
'.mp4': 'MP4 Video',
'.avi': 'AVI Video',
'.mov': 'QuickTime Video',
// Other
'.exe': 'Windows Executable',
'.dll': 'Dynamic Link Library',
'.so