Date: October 30, 2025 Project: f5-corkscrew v1.5.0 Coverage: 95.5% statements, 89.74% branches
The codebase is generally well-structured with excellent test coverage (95.5%). The main areas for improvement include code organization in large files, TypeScript type safety, error handling patterns, and removing technical debt from commented code.
Issue: Several files are exceptionally large and handle multiple responsibilities.
Files requiring refactoring:
deepParse.ts(897 lines) - Contains parsing logic for multiple TMOS object typesxmlStats.ts(608 lines) - XML parsing and statistics processingltm.ts(514 lines) - Main BigipConfig class with multiple responsibilitiesmodels.ts(443 lines) - All TypeScript type definitions
Recommendations:
-
Split
deepParse.tsby object type:src/parsers/ ├── gtmParser.ts # GTM server, wideip, pool parsing ├── ltmParser.ts # LTM virtual, pool, node parsing ├── apmParser.ts # APM policy parsing ├── asmParser.ts # ASM/WAF parsing └── parseDeep.ts # Main coordinator/dispatcher -
Split
models.tsby domain:src/models/ ├── ltmModels.ts # LTM-specific types ├── gtmModels.ts # GTM-specific types ├── commonModels.ts # Shared types (ConfigFile, Stats, etc.) └── index.ts # Re-export all types -
Extract responsibilities from
ltm.ts:- Move parsing logic to dedicated parser classes
- Extract file handling to separate service
- Keep BigipConfig as a clean facade/orchestrator
Benefits:
- Improved maintainability
- Easier testing of individual components
- Better code navigation
- Reduced merge conflicts
Current issues:
parseDeep(obj: any, rx: RegExTree)- Line 13 of deepParse.ts- Multiple
anytypes in logger.ts and xmlStats.ts - ESLint disables for
@typescript-eslint/no-explicit-any
Recommendations:
-
Create specific interfaces for parsed objects:
interface ParseableObject { gtm?: GtmConfig; ltm?: LtmConfig; apm?: ApmConfig; // ... } export async function parseDeep(obj: ParseableObject, rx: RegExTree): Promise<void>
-
Replace logger
anywithunknown:// Current: private _journal: any = []; private _journal: string[] = []; // Current: write(label: string, ...messageParts: unknown[]) // This is actually correct! Just remove the eslint-disable
-
Create type guards for runtime type checking:
function isGtmServer(obj: unknown): obj is { gtm: { server: Record<string, unknown> } } { return typeof obj === 'object' && obj !== null && 'gtm' in obj; }
Files with extensive commented code:
logger.ts- Lines 18-47 contain old logging implementationdeepParse.ts- Lines 25, 38, 49, 62-65 contain commented sections- Multiple TODOs in comments
Recommendations:
- Remove all commented-out code (use git history if needed)
- Convert meaningful TODOs to GitHub issues
- Remove explanatory comments that are now obsolete
Example cleanup in logger.ts:
// REMOVE lines 18-47 (old verbose/log methods)
// REMOVE lines 107-118 (old data2String method)
// Keep only active codeCurrent TODOs found:
objCounter.ts:20- "dig deeper to get better count of actual profile numbers"objCounter.ts:38- "same as profiles, dig deeper for all monitor keys"tests/archive_generator/archiveBuilder.ts:19-21- "loop through files and update tmos version"
Recommendations:
- Create GitHub issues for each TODO
- Prioritize and schedule work
- Remove inline TODOs and reference issue numbers in commit messages
Current patterns:
- Some functions use try/catch with logger.error
- Some use promises with .catch()
- Errors are swallowed in many places
Recommendations:
-
Create centralized error handling:
// src/errors/CorkscrewError.ts export class CorkscrewError extends Error { constructor( message: string, public code: string, public context?: Record<string, unknown> ) { super(message); this.name = 'CorkscrewError'; } } export class ParseError extends CorkscrewError { constructor(message: string, context?: Record<string, unknown>) { super(message, 'PARSE_ERROR', context); } }
-
Standardize error handling in async functions:
async parseConf(conf: ConfigFile): Promise<void> { try { // parsing logic } catch (error) { const parseError = new ParseError( `Failed to parse config file: ${conf.fileName}`, { fileName: conf.fileName, originalError: error } ); logger.error(parseError.message, parseError.context); throw parseError; } }
Current issues:
- Generic error messages like "not able to read file"
- Missing context about what operation failed
Recommendations:
// Instead of:
throw new Error(`not able to read file => ${e.message}`);
// Use:
throw new Error(
`Failed to read config file "${filePath.base}" from "${filePath.dir}": ${e.message}`
);Current state:
- Tests are well-organized and comprehensive (95.5% coverage)
- Numbered test files (010_, 020_, etc.) make execution order clear
- Some test files are large (327 lines in 010_json_objects.test.ts)
Recommendations:
-
Add test helpers to reduce duplication:
// tests/helpers/fixtures.ts export async function loadTestArchive(type: 'ucs' | 'qkview' | 'conf') { const archivePath = await archiveMake(type); const device = new BigipConfig(); await device.loadParseAsync(archivePath); return device; }
-
Group related assertions:
// Instead of multiple individual tests, use describe blocks describe('Virtual Server Parsing', () => { describe('Basic Properties', () => { it('should parse destination', () => { ... }); it('should parse pool reference', () => { ... }); }); describe('Complex Configurations', () => { it('should handle missing destination', () => { ... }); it('should handle multiple profiles', () => { ... }); }); });
Recommendations:
- Add end-to-end CLI tests with actual file I/O
- Add performance benchmarks for large configs
- Test error scenarios more thoroughly
Stats from README:
- 6MB config, 223k lines, 13k objects: ~20 seconds
- Target: Acceptable up to a couple of minutes
Recommendations:
-
Profile hot paths:
node --prof dist/cli.js --file large-config.ucs node --prof-process isolate-*.log > profile.txt
-
Consider Worker Threads for parallel parsing:
// For multiple partition configs, parse in parallel import { Worker } from 'worker_threads'; async function parsePartitionsParallel(partitions: ConfigFile[]) { const workers = partitions.map(p => new Worker('./partitionWorker.js', { workerData: p }) ); return Promise.all(workers); }
-
Optimize regex compilation:
// Cache compiled regexes in RegExTree private _compiledCache = new Map<string, RegExp>(); getRegex(key: string): RegExp { if (!this._compiledCache.has(key)) { this._compiledCache.set(key, new RegExp(this.patterns[key])); } return this._compiledCache.get(key)!; }
Current state:
- Some functions have excellent JSDoc (BigipConfig class)
- Many utility functions lack documentation
- Complex parsing logic needs more explanation
Recommendations:
-
Document all exported functions:
/** * Parses TMOS configuration objects into structured JSON. * Handles deep parsing of GTM, LTM, APM, and ASM objects. * * @param obj - The configuration object to parse * @param rx - RegExTree containing version-specific regex patterns * @throws {ParseError} If object structure is invalid * @returns Promise that resolves when parsing is complete * * @example * ```typescript * const obj = { gtm: { server: { ... } } }; * await parseDeep(obj, regexTree); * console.log(obj.gtm.server); // Now fully parsed * ``` */ export async function parseDeep(obj: any, rx: RegExTree): Promise<void>
-
Add architecture decision records (ADRs):
docs/adr/ ├── 001-use-streaming-for-archives.md ├── 002-json-vs-full-parse.md └── 003-regex-tree-structure.md
Issue: The decompress package (v4.2.1) is not actively used in the codebase.
Findings:
# Search shows decompress imported but not used:
grep -r "decompress" src/
# Only found in @types/decompress, not actual codeRecommendation:
- Verify if decompress is actually used
- If not, remove it:
npm uninstall decompress @types/decompress - The project already uses
tar-stream+zlibfor archive extraction
Current usage: Very limited usage of object-path package.
Recommendation:
- Review if native JavaScript object access is sufficient
- Modern TypeScript provides good type safety for object access
- Consider implementing simple helper functions instead
Current state:
- Magic numbers in code (timeout: 120000, coverage thresholds: 50%)
- Regex patterns hardcoded in regex.ts
Recommendations:
-
Create config file:
// src/config.ts export const config = { timeout: { parse: 120000, test: 120000 }, coverage: { statements: 50, branches: 50, functions: 50, lines: 50 }, performance: { maxConfigSize: 50 * 1024 * 1024, // 50MB maxObjects: 100000 } };
-
Support environment variables:
export const config = { logLevel: process.env.F5_CORKSCREW_LOG_LEVEL || 'info', debug: process.env.F5_CORKSCREW_DEBUG === 'true' };
Recommendation:
npm install --save-dev husky lint-staged
# package.json
{
"lint-staged": {
"src/**/*.ts": [
"eslint --fix",
"prettier --write"
],
"tests/**/*.ts": [
"eslint --fix"
]
}
}Current state: No code formatting standard enforced
Recommendation:
npm install --save-dev prettier
# .prettierrc.json
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2
}Current state:
- File store includes certificates and keys
- License file parsing
Recommendations:
-
Add option to sanitize output:
interface ExplodeOptions { sanitize?: boolean; redactLicenseKeys?: boolean; excludePrivateKeys?: boolean; }
-
Add warning for sensitive data:
if (this.fileStore.some(f => f.fileName.includes('key'))) { logger.warn('Config contains private keys - ensure secure handling'); }
Current state: File paths are used from archives without validation
Recommendation:
import path from 'path';
function sanitizePath(filePath: string): string {
const normalized = path.normalize(filePath);
if (normalized.includes('..') || path.isAbsolute(normalized)) {
throw new Error(`Unsafe path detected: ${filePath}`);
}
return normalized;
}Current limitations:
- Only JSON output
- Limited progress feedback
- No interactive mode
Recommendations:
-
Add progress bars:
npm install --save-dev cli-progress
-
Add output formats:
// --format=json|yaml|table yargs.options({ format: { choices: ['json', 'yaml', 'table'], default: 'json' } });
-
Add verbose mode:
// --verbose or -v flag if (args.verbose) { logger.setLevel('debug'); }
- ✅ Remove commented code in logger.ts and other files
- ✅ Replace
anytypes with proper TypeScript types - ✅ Add centralized error handling
- ✅ Document public APIs with JSDoc
- Refactor large files (deepParse.ts, models.ts)
- Add pre-commit hooks and Prettier
- Implement progress feedback in CLI
- Add integration tests
- Consider Worker Threads for parallel parsing
- Add performance benchmarks
- Create architecture decision records
- Implement sanitization options
The f5-corkscrew codebase is solid with excellent test coverage. The recommendations focus on:
- Code maintainability: Splitting large files, improving types
- Developer experience: Better error messages, documentation
- Future scalability: Performance optimizations, better architecture
Most recommendations are non-breaking and can be implemented incrementally.