Replaces source-level JavaScript function tracing with Babel AST
transformation via babel-tracer-plugin.js and trace-runner.js. Adds
replay test generation, Python-side tracer runner, and --language
flag to the tracer CLI for explicit JS/TS routing.
Replace `uv tool install` (which installs globally into ~/.local/bin) with
a dedicated venv at an OS-specific cache directory (~/.cache/codeflash/venv
on Linux, ~/Library/Caches/codeflash/venv on macOS, %LOCALAPPDATA%\codeflash\venv
on Windows). The CLI entry point now invokes the binary directly from the venv
instead of via `uv tool run`. Also strips VIRTUAL_ENV/CONDA env vars from child
processes to avoid interference from activated environments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The V8 serialization self-test used `instanceof Map` which fails in
Jest's sandboxed VM context where the Map class from v8.deserialize
differs from the test environment's Map class. This incorrectly
disabled V8 serialization, falling through to msgpack which often
also isn't resolvable in monorepo setups.
Replaced all `instanceof` checks in the serializer with
`Object.prototype.toString.call()` for cross-VM-context compatibility,
matching the pattern already used in the msgpack codepath.
Trace IDs: 003e1410, fe2ae122, fde51112 (153 affected logs)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The recursive search in loop-runner.js wasn't entering package
directories inside scoped namespaces (e.g. @jest/core), so it couldn't
find jest-runner nested at @jest/core/node_modules/jest-runner.
Upgrade Python deps via uv sync --upgrade (werkzeug, filelock for py>=3.10,
and others). Run npm audit fix across JS test fixtures to patch minimatch
and rollup vulnerabilities.
Remaining unfixable:
- filelock <3.20.3 for py<3.10 (patched version requires py>=3.10)
- serialize-javascript in mocha ^10 (fix requires mocha 11 breaking change)
Root cause: Vitest performance tests reported "20.0 seconds over 1 loop"
(JUnit XML wall-clock fallback) instead of actual per-function nanosecond
timing. This was a chain of two issues:
1. **stdout interception**: Vitest's default `threads` pool intercepts
process.stdout.write() and console.log(), preventing timing markers
from flowing to the parent process. Fixed by adding `--pool=forks`
to all Vitest commands and config files. The `forks` pool uses child
processes where stdout flows directly to the parent.
2. **test name detection**: Even after markers flowed through (43,000+
found in stdout), the parser couldn't match them to JUnit XML
testcases because all markers had "unknown" as the test name. This
happened because Vitest doesn't inject `beforeEach` as a global
(unlike Jest), so capture.js's Jest-style hook to set
`currentTestName` never fired.
Fixed by adding Vitest-specific test name detection in capture.js:
- Primary: `expect.getState().currentTestName` (full describe path)
- Fallback: `__vitest_worker__.current.fullTestName`
- Defense-in-depth: parser fallback matches "unknown" markers to
the first testcase when no name match is found
Result: cheerio's `isHtml` went from "20.0s / 1 loop" to
"902μs / 20,853 loops" with proper speedup analysis.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ship a zero-dependency jest-reporter.js inside the codeflash runtime package
instead of requiring the external jest-junit npm package. This ensures the
reporter is always available when codeflash is installed, fixing Jest-based
projects (Strapi, Moleculer) that failed because jest-junit wasn't installed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The loop-runner was loading jest-runner from codeflash's node_modules (v29)
instead of the project's (v30), causing "runtime.enterTestCode is not a function"
errors. This fix:
- Adds recursive search to find jest-runner in any node_modules structure
- Works with npm, yarn, and pnpm (including non-hoisted deps)
- Prefers higher versions when multiple are found
- Removes internal looping in capturePerf when using external loop-runner
- Creates fresh TestRunner per batch to avoid Jest 30 state corruption
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Loop index now represents how many times all test files ran (batch count)
instead of per-invocation index. Also fixes Date.now() usage when random
seed is active and removes JS-specific workaround in number_of_loops.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Improvements to loop-runner.js:
- Extract isValidJestRunnerPath() helper to reduce code duplication
- Add comprehensive JSDoc comments for Jest version detection
- Improve error messages with more context about detected versions
- Add better documentation for runTests() method
- Add validation for TestRunner class availability in Jest 30
Improvements to capture.js:
- Extract _recordAsyncTiming() helper to reduce duplication
- Add comprehensive JSDoc for _capturePerfAsync() with all parameters
- Improve error handling in async looping (record timing before throwing)
- Enhance shouldStopStability() documentation with algorithm details
- Improve code organization with clearer comments
These changes improve maintainability and debugging without changing behavior.
After merging main, constants like PERF_STABILITY_CHECK, PERF_MIN_LOOPS,
PERF_LOOP_COUNT were changed to getter functions. Updated all references
in capturePerf and _capturePerfAsync to use the getter function calls.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Jest 30 compatibility by detecting version and using TestRunner class
- Resolve jest-runner from project's node_modules instead of codeflash's bundle
- Fix time limit enforcement by using local time tracking instead of shared state
(Jest runs tests in worker processes, so state isn't shared with runner)
- Integrate stability-based early stopping into capturePerf
- Use plain object instead of Set for stableInvocations to survive Jest module resets
- Fix async function benchmarking: properly loop through iterations using async helper
(Previously, async functions only got one timing marker due to early return)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Instead of duplicating monorepo detection logic in JavaScript, leverage
the existing Python _find_node_project_root and add _find_monorepo_root
functions. Pass the detected monorepo root via CODEFLASH_MONOREPO_ROOT
environment variable to the loop-runner.
This approach:
- Reuses existing Python project detection logic
- Provides a reliable monorepo root hint to JavaScript
- Maintains fallback directory traversal for edge cases
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added resolveJestRunner() function that walks up the directory tree to
find jest-runner in workspace root node_modules. This fixes the
"jest-runner requires jest-runner to be installed" error when running
in monorepo packages.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Vitest caches modules and may load capture.js before environment
variables like CODEFLASH_PERF_LOOP_COUNT are set. When these were
read as constants at module load time, they would always return
default values.
This change converts the performance configuration from constants
to getter functions that read environment variables at runtime,
ensuring correct values are used even when the module is cached.
Fixes:
- PERF_LOOP_COUNT always being 1 in Vitest
- PERF_BATCH_SIZE, PERF_MIN_LOOPS, etc. using defaults instead of env values
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously, MAX_BATCHES was set to PERF_LOOP_COUNT directly (e.g., 250),
which caused the loop-runner to run 250 batches even though only 25
batches were needed to produce timing data (with BATCH_SIZE=10).
The bug was that timing markers only appeared for the first N batches
(where N = LOOP_COUNT / BATCH_SIZE), and the remaining batches were
wasted overhead.
Fix: Calculate MAX_BATCHES as ceil(LOOP_COUNT / BATCH_SIZE) + 1, capped
at LOOP_COUNT. This ensures only the necessary batches run:
- With LOOP_COUNT=250, BATCH_SIZE=10: MAX_BATCHES = 26 (not 250)
This significantly improves benchmark efficiency by eliminating wasted
Jest passes that don't contribute timing data.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>