Remove debugging statements or use a logging library
console.log(
1'use strict';23require('@babel/register')({4 presets: [['@babel/preset-react', {runtime: 'automatic'}]],5 plugins: ['@babel/plugin-transform-modules-commonjs'],6 only: [/\/src\//],7});89const path = require('path');10const fs = require('fs');11const webpack = require('webpack');12const inspector = require('node:inspector');1314const {clientManifest, ssrManifest} = require('./webpack-mock');1516const PROFILE_MODE = process.argv.includes('--profile');17const CONCURRENT_MODE = process.argv.includes('--concurrent');18const INJECT = !process.argv.includes('--no-injection');19const JSON_OUT = (function () {20 const arg = process.argv.find(function (a) {21 return a.startsWith('--json-out=');22 });23 return arg ? arg.slice('--json-out='.length) : null;24})();25const jsonResults = [];26function writeJsonOut(mode) {27 if (!JSON_OUT) return;28 fs.writeFileSync(JSON_OUT, JSON.stringify({mode, results: jsonResults}));29}3031// ---------------------------------------------------------------------------32// Build33// ---------------------------------------------------------------------------3435function build() {36 const config = require('./webpack.config');37 return new Promise(function (resolve, reject) {38 webpack(config, function (err, stats) {39 if (err) {40 reject(err);41 return;42 }43 if (stats.hasErrors()) {44 reject(new Error(stats.toString({errors: true})));45 return;46 }47 console.log(48 stats.toString({colors: true, modules: false, entrypoints: false})49 );50 resolve();51 });52 });53}5455// ---------------------------------------------------------------------------56// Render helpers57// ---------------------------------------------------------------------------5859const {60 renderFizzNode: renderFizzNodeStream,61 renderFizzEdge: renderFizzEdgeStream,62 renderFlightFizzNode: renderFlightFizzNodeStream,63 renderFlightFizzEdge: renderFlightFizzEdgeStream,64 nodeStreamToString,65 webStreamToString,66} = require('./render-helpers');67const {printGrid} = require('./print-helpers');6869function renderFizzNode(AppComponent, itemCount) {70 return nodeStreamToString(renderFizzNodeStream(AppComponent, itemCount));71}7273function renderFizzEdge(AppComponent, itemCount) {74 return renderFizzEdgeStream(AppComponent, itemCount).then(webStreamToString);75}7677function renderFlightFizzNode(renderRSCNode, AppComponent, itemCount) {78 return nodeStreamToString(79 renderFlightFizzNodeStream(80 renderRSCNode,81 AppComponent,82 itemCount,83 clientManifest,84 ssrManifest,85 {inject: INJECT}86 )87 );88}8990function renderFlightFizzEdge(renderRSCEdge, AppComponent, itemCount) {91 return renderFlightFizzEdgeStream(92 renderRSCEdge,93 AppComponent,94 itemCount,95 clientManifest,96 ssrManifest,97 {inject: INJECT}98 ).then(webStreamToString);99}100101// ---------------------------------------------------------------------------102// Benchmarking103// ---------------------------------------------------------------------------104105const canGC = typeof globalThis.gc === 'function';106107// Yield to the event loop's check phase between renders. React schedules a108// setImmediate per request, but a fully in-process render completes entirely109// in microtasks/nextTicks, so a tight benchmark loop never lets those110// immediates run. They then pile up in Node's immediate queue, each one111// retaining its (otherwise finished) request graph, which inflates any memory112// measurement. A real server yields to the event loop on every request, so113// draining between iterations is both correct and more representative.114function tick() {115 return new Promise(resolve => setImmediate(resolve));116}117118async function settledHeapUsed() {119 await tick();120 if (canGC) globalThis.gc();121 return process.memoryUsage().heapUsed;122}123124async function runBenchmark(name, fn, iterations, warmup) {125 if (canGC) globalThis.gc();126 const heapBefore = await settledHeapUsed();127128 // Warmup129 for (let i = 0; i < warmup; i++) {130 await fn();131 await tick();132 }133134 // Collect GC pauses during timed iterations.135 let gcCount = 0;136 let gcTotalMs = 0;137 const gcObs = new PerformanceObserver(list => {138 for (const entry of list.getEntries()) {139 gcCount++;140 gcTotalMs += entry.duration;141 }142 });143 gcObs.observe({entryTypes: ['gc']});144145 // Timed iterations. The tick between iterations is excluded from the146 // timed window.147 const times = [];148 for (let i = 0; i < iterations; i++) {149 const start = performance.now();150 await fn();151 times.push(performance.now() - start);152 await tick();153 }154 gcObs.disconnect();155 const heapAfter = await settledHeapUsed();156157 // Trim top/bottom 5% to remove outliers158 const sorted = [...times].sort((a, b) => a - b);159 const trimCount = Math.floor(sorted.length * 0.05);160 const trimmed = sorted.slice(trimCount, sorted.length - trimCount);161162 const mean = trimmed.reduce((s, t) => s + t, 0) / trimmed.length;163 const median = sorted[Math.floor(sorted.length / 2)];164 const stddev = Math.sqrt(165 trimmed.reduce((s, t) => s + (t - mean) ** 2, 0) / trimmed.length166 );167 const p95 = sorted[Math.floor(sorted.length * 0.95)];168 const min = sorted[0];169 const max = sorted[sorted.length - 1];170171 return {172 name,173 mean,174 median,175 stddev,176 p95,177 min,178 max,179 iterations,180 gcCount,181 gcTotalMs,182 heapBefore,183 heapAfter,184 };185}186187function printResult(result) {188 jsonResults.push(result);189 console.log(' %s:', result.name);190 console.log(' Mean: %s ms', result.mean.toFixed(2));191 console.log(' Median: %s ms', result.median.toFixed(2));192 console.log(' Stddev: %s ms', result.stddev.toFixed(2));193 console.log(' P95: %s ms', result.p95.toFixed(2));194 console.log(' Min: %s ms', result.min.toFixed(2));195 console.log(' Max: %s ms', result.max.toFixed(2));196 console.log(197 ' GC: %d pauses, %s ms total (%s ms/iter)',198 result.gcCount,199 result.gcTotalMs.toFixed(1),200 (result.gcTotalMs / result.iterations).toFixed(2)201 );202 printHeap(result);203}204205function printHeap(result) {206 if (!canGC) return;207 const mb = b => (b / 1048576).toFixed(1);208 const delta = result.heapAfter - result.heapBefore;209 console.log(210 ' Heap: %s MB retained after run (%s%s MB vs before)',211 mb(result.heapAfter),212 delta >= 0 ? '+' : '',213 mb(delta)214 );215}216217async function runConcurrent(name, fn, total, concurrency, warmup) {218 if (canGC) globalThis.gc();219 const heapBefore = await settledHeapUsed();220221 for (let i = 0; i < warmup; i++) {222 await fn();223 await tick();224 }225226 let gcCount = 0;227 let gcTotalMs = 0;228 const gcObs = new PerformanceObserver(list => {229 for (const entry of list.getEntries()) {230 gcCount++;231 gcTotalMs += entry.duration;232 }233 });234 gcObs.observe({entryTypes: ['gc']});235236 const latencies = new Array(total);237 let completed = 0;238 let launched = 0;239240 const start = performance.now();241 await new Promise(resolve => {242 function launch() {243 while (launched < total && launched - completed < concurrency) {244 const idx = launched++;245 const t0 = performance.now();246 fn()247 .then(() => {248 latencies[idx] = performance.now() - t0;249 // Drain immediates before freeing the slot (see tick()).250 return tick();251 })252 .then(() => {253 completed++;254 if (completed === total) {255 resolve();256 } else {257 launch();258 }259 });260 }261 }262 launch();263 });264 const elapsed = performance.now() - start;265 gcObs.disconnect();266 const heapAfter = await settledHeapUsed();267268 const sorted = [...latencies].sort((a, b) => a - b);269 const mean = sorted.reduce((s, t) => s + t, 0) / sorted.length;270 const p95 = sorted[Math.floor(sorted.length * 0.95)];271272 return {273 name,274 reqPerSec: (total / elapsed) * 1000,275 mean,276 p95,277 total,278 concurrency,279 gcCount,280 gcTotalMs,281 heapBefore,282 heapAfter,283 };284}285286function printConcurrentResult(result) {287 jsonResults.push(result);288 console.log(' %s:', result.name);289 console.log(' Req/s: %s', result.reqPerSec.toFixed(1));290 console.log(' Mean: %s ms', result.mean.toFixed(2));291 console.log(' P95: %s ms', result.p95.toFixed(2));292 console.log(293 ' GC: %d pauses, %s ms total (%s ms/req)',294 result.gcCount,295 result.gcTotalMs.toFixed(1),296 (result.gcTotalMs / result.total).toFixed(2)297 );298 printHeap(result);299}300301// ---------------------------------------------------------------------------302// CPU Profiling303// ---------------------------------------------------------------------------304305function startProfiler() {306 const session = new inspector.Session();307 session.connect();308 return new Promise(function (resolve, reject) {309 session.post('Profiler.enable', function (err) {310 if (err) {311 reject(err);312 return;313 }314 session.post('Profiler.start', function (err2) {315 if (err2) {316 reject(err2);317 return;318 }319 resolve(session);320 });321 });322 });323}324325function stopProfiler(session, outputPath) {326 return new Promise(function (resolve, reject) {327 session.post('Profiler.stop', function (err, {profile}) {328 if (err) {329 reject(err);330 return;331 }332 fs.mkdirSync(path.dirname(outputPath), {recursive: true});333 fs.writeFileSync(outputPath, JSON.stringify(profile));334 session.post('Profiler.disable');335 session.disconnect();336 resolve(profile);337 });338 });339}340341function printTopFunctions(profile, topN) {342 // Aggregate self-time per function from the profile nodes.343 const selfTimes = new Map();344 for (const node of profile.nodes) {345 const name = node.callFrame.functionName || '(anonymous)';346 const loc = node.callFrame.url347 ? node.callFrame.url.replace(/.*\//, '') + ':' + node.callFrame.lineNumber348 : '(native)';349 const key = name + ' @ ' + loc;350 const hitCount = node.hitCount || 0;351 selfTimes.set(key, (selfTimes.get(key) || 0) + hitCount);352 }353354 const sorted = [...selfTimes.entries()]355 .sort((a, b) => b[1] - a[1])356 .slice(0, topN);357358 const totalSamples = profile.nodes.reduce((s, n) => s + (n.hitCount || 0), 0);359360 console.log(' Top %d functions by self-time:', topN);361 for (const [key, hits] of sorted) {362 const pct = ((hits / totalSamples) * 100).toFixed(1);363 console.log(' %s%% - %s', pct, key);364 }365}366367async function profileRun(name, fn, warmup, iterations, outputPath) {368 // Warmup (unprofiled)369 for (let i = 0; i < warmup; i++) {370 await fn();371 await tick();372 }373374 // Collect GC pauses during the profiled run.375 let gcCount = 0;376 let gcTotalMs = 0;377 const gcObs = new PerformanceObserver(list => {378 for (const entry of list.getEntries()) {379 gcCount++;380 gcTotalMs += entry.duration;381 }382 });383 gcObs.observe({entryTypes: ['gc']});384385 // Profiled run386 const session = await startProfiler();387 for (let i = 0; i < iterations; i++) {388 await fn();389 await tick();390 }391 const profile = await stopProfiler(session, outputPath);392 gcObs.disconnect();393394 console.log(' %s → %s', name, outputPath);395 printTopFunctions(profile, 10);396 console.log(397 ' GC: %d pauses, %s ms total (%s ms/iter)',398 gcCount,399 gcTotalMs.toFixed(1),400 (gcTotalMs / iterations).toFixed(2)401 );402}403404// ---------------------------------------------------------------------------405// Main406// ---------------------------------------------------------------------------407408async function main() {409 console.log('Building RSC bundle...\n');410 await build();411412 const {413 renderRSCNode,414 renderRSCEdge,415 App: RSCApp,416 AppAsync: RSCAppAsync,417 } = require('./build/rsc-bundle.js');418 const App = require('./src/App.js').default;419 const AppAsync = require('./src/AppAsync.js').default;420421 const ITEM_COUNT = 200;422423 const WARMUP = 50;424 const ITERATIONS = 1000;425 const PROFILE_WARMUP = 50;426 const PROFILE_ITERATIONS = 500;427428 // --- Verify renders ---429 console.log('\n--- Verifying renders ---\n');430431 const fizzNodeHtml = await renderFizzNode(App, ITEM_COUNT);432 console.log('Fizz (Node, sync): %d bytes', fizzNodeHtml.length);433434 const flightFizzNodeHtml = await renderFlightFizzNode(435 renderRSCNode,436 RSCApp,437 ITEM_COUNT438 );439 console.log(440 'Flight + Fizz (Node, sync): %d bytes',441 flightFizzNodeHtml.length442 );443444 const fizzNodeAsyncHtml = await renderFizzNode(AppAsync, ITEM_COUNT);445 console.log('Fizz (Node, async): %d bytes', fizzNodeAsyncHtml.length);446447 const flightFizzNodeAsyncHtml = await renderFlightFizzNode(448 renderRSCNode,449 RSCAppAsync,450 ITEM_COUNT451 );452 console.log(453 'Flight + Fizz (Node, async):%d bytes',454 flightFizzNodeAsyncHtml.length455 );456457 const fizzEdgeHtml = await renderFizzEdge(App, ITEM_COUNT);458 console.log('Fizz (Edge, sync): %d bytes', fizzEdgeHtml.length);459460 const fizzEdgeAsyncHtml = await renderFizzEdge(AppAsync, ITEM_COUNT);461 console.log('Fizz (Edge, async): %d bytes', fizzEdgeAsyncHtml.length);462463 const flightFizzEdgeHtml = await renderFlightFizzEdge(464 renderRSCEdge,465 RSCApp,466 ITEM_COUNT467 );468 console.log(469 'Flight + Fizz (Edge, sync): %d bytes',470 flightFizzEdgeHtml.length471 );472473 const flightFizzEdgeAsyncHtml = await renderFlightFizzEdge(474 renderRSCEdge,475 RSCAppAsync,476 ITEM_COUNT477 );478 console.log(479 'Flight + Fizz (Edge, async):%d bytes',480 flightFizzEdgeAsyncHtml.length481 );482483 // --- CPU Profiling ---484 if (PROFILE_MODE) {485 console.log(486 '\n--- CPU Profiling (%d warmup, %d iterations) ---\n',487 PROFILE_WARMUP,488 PROFILE_ITERATIONS489 );490491 const profileDir = path.resolve(__dirname, 'build/profiles');492493 await profileRun(494 'Fizz (Node, sync)',495 () => renderFizzNode(App, ITEM_COUNT),496 PROFILE_WARMUP,497 PROFILE_ITERATIONS,498 path.join(profileDir, 'fizz-node-sync.cpuprofile')499 );500501 await profileRun(502 'Flight + Fizz (Node, sync)',503 () => renderFlightFizzNode(renderRSCNode, RSCApp, ITEM_COUNT),504 PROFILE_WARMUP,505 PROFILE_ITERATIONS,506 path.join(profileDir, 'flight-fizz-node-sync.cpuprofile')507 );508509 await profileRun(510 'Fizz (Node, async)',511 () => renderFizzNode(AppAsync, ITEM_COUNT),512 PROFILE_WARMUP,513 PROFILE_ITERATIONS,514 path.join(profileDir, 'fizz-node-async.cpuprofile')515 );516517 await profileRun(518 'Flight + Fizz (Node, async)',519 () => renderFlightFizzNode(renderRSCNode, RSCAppAsync, ITEM_COUNT),520 PROFILE_WARMUP,521 PROFILE_ITERATIONS,522 path.join(profileDir, 'flight-fizz-node-async.cpuprofile')523 );524525 await profileRun(526 'Fizz (Edge, sync)',527 () => renderFizzEdge(App, ITEM_COUNT),528 PROFILE_WARMUP,529 PROFILE_ITERATIONS,530 path.join(profileDir, 'fizz-edge-sync.cpuprofile')531 );532533 await profileRun(534 'Flight + Fizz (Edge, sync)',535 () => renderFlightFizzEdge(renderRSCEdge, RSCApp, ITEM_COUNT),536 PROFILE_WARMUP,537 PROFILE_ITERATIONS,538 path.join(profileDir, 'flight-fizz-edge-sync.cpuprofile')539 );540541 await profileRun(542 'Fizz (Edge, async)',543 () => renderFizzEdge(AppAsync, ITEM_COUNT),544 PROFILE_WARMUP,545 PROFILE_ITERATIONS,546 path.join(profileDir, 'fizz-edge-async.cpuprofile')547 );548549 await profileRun(550 'Flight + Fizz (Edge, async)',551 () => renderFlightFizzEdge(renderRSCEdge, RSCAppAsync, ITEM_COUNT),552 PROFILE_WARMUP,553 PROFILE_ITERATIONS,554 path.join(profileDir, 'flight-fizz-edge-async.cpuprofile')555 );556557 console.log(558 '\nProfiles saved to build/profiles/. Open in Chrome DevTools or speedscope.app.'559 );560561 return;562 }563564 // --- Concurrent Benchmark ---565 if (CONCURRENT_MODE) {566 const CONCURRENCY = 50;567 const TOTAL = 1000;568 const CONC_WARMUP = 20;569570 console.log(571 '\n--- Concurrent Benchmark (%d warmup, %d concurrency, %d requests, %d items) ---\n',572 CONC_WARMUP,573 CONCURRENCY,574 TOTAL,575 ITEM_COUNT576 );577578 const fizzNodeSync = await runConcurrent(579 'Fizz (Node, sync)',580 () => renderFizzNode(App, ITEM_COUNT),581 TOTAL,582 CONCURRENCY,583 CONC_WARMUP584 );585 printConcurrentResult(fizzNodeSync);586587 const flightFizzNodeSync = await runConcurrent(588 'Flight + Fizz (Node, sync)',589 () => renderFlightFizzNode(renderRSCNode, RSCApp, ITEM_COUNT),590 TOTAL,591 CONCURRENCY,592 CONC_WARMUP593 );594 printConcurrentResult(flightFizzNodeSync);595596 const fizzNodeAsync = await runConcurrent(597 'Fizz (Node, async)',598 () => renderFizzNode(AppAsync, ITEM_COUNT),599 TOTAL,600 CONCURRENCY,601 CONC_WARMUP602 );603 printConcurrentResult(fizzNodeAsync);604605 const flightFizzNodeAsync = await runConcurrent(606 'Flight + Fizz (Node, async)',607 () => renderFlightFizzNode(renderRSCNode, RSCAppAsync, ITEM_COUNT),608 TOTAL,609 CONCURRENCY,610 CONC_WARMUP611 );612 printConcurrentResult(flightFizzNodeAsync);613614 const fizzEdgeSync = await runConcurrent(615 'Fizz (Edge, sync)',616 () => renderFizzEdge(App, ITEM_COUNT),617 TOTAL,618 CONCURRENCY,619 CONC_WARMUP620 );621 printConcurrentResult(fizzEdgeSync);622623 const flightFizzEdgeSync = await runConcurrent(624 'Flight + Fizz (Edge, sync)',625 () => renderFlightFizzEdge(renderRSCEdge, RSCApp, ITEM_COUNT),626 TOTAL,627 CONCURRENCY,628 CONC_WARMUP629 );630 printConcurrentResult(flightFizzEdgeSync);631632 const fizzEdgeAsync = await runConcurrent(633 'Fizz (Edge, async)',634 () => renderFizzEdge(AppAsync, ITEM_COUNT),635 TOTAL,636 CONCURRENCY,637 CONC_WARMUP638 );639 printConcurrentResult(fizzEdgeAsync);640641 const flightFizzEdgeAsync = await runConcurrent(642 'Flight + Fizz (Edge, async)',643 () => renderFlightFizzEdge(renderRSCEdge, RSCAppAsync, ITEM_COUNT),644 TOTAL,645 CONCURRENCY,646 CONC_WARMUP647 );648 printConcurrentResult(flightFizzEdgeAsync);649650 const rps = r => r.reqPerSec;651652 console.log('\n--- Flight overhead ---\n');653 printGrid(654 ['Fizz', 'Flight+Fizz'],655 [656 ['Node sync', fizzNodeSync, flightFizzNodeSync],657 ['Node async', fizzNodeAsync, flightFizzNodeAsync],658 ['Edge sync', fizzEdgeSync, flightFizzEdgeSync],659 ['Edge async', fizzEdgeAsync, flightFizzEdgeAsync],660 ],661 rps,662 'req/s',663 'higher is better'664 );665666 console.log('\n--- Edge vs Node ---\n');667 printGrid(668 ['Node', 'Edge'],669 [670 ['Fizz sync', fizzNodeSync, fizzEdgeSync],671 ['Fizz async', fizzNodeAsync, fizzEdgeAsync],672 ['Flight+Fizz sync', flightFizzNodeSync, flightFizzEdgeSync],673 ['Flight+Fizz async', flightFizzNodeAsync, flightFizzEdgeAsync],674 ],675 rps,676 'req/s',677 'higher is better'678 );679680 writeJsonOut('concurrent');681 return;682 }683684 // --- Benchmark ---685 console.log(686 '\n--- Benchmark (%d warmup, %d iterations, %d items) ---\n',687 WARMUP,688 ITERATIONS,689 ITEM_COUNT690 );691692 const fizzNodeSync = await runBenchmark(693 'Fizz (Node, sync)',694 () => renderFizzNode(App, ITEM_COUNT),695 ITERATIONS,696 WARMUP697 );698 printResult(fizzNodeSync);699700 const flightFizzNodeSync = await runBenchmark(701 'Flight + Fizz (Node, sync)',702 () => renderFlightFizzNode(renderRSCNode, RSCApp, ITEM_COUNT),703 ITERATIONS,704 WARMUP705 );706 printResult(flightFizzNodeSync);707708 const fizzNodeAsync = await runBenchmark(709 'Fizz (Node, async)',710 () => renderFizzNode(AppAsync, ITEM_COUNT),711 ITERATIONS,712 WARMUP713 );714 printResult(fizzNodeAsync);715716 const flightFizzNodeAsync = await runBenchmark(717 'Flight + Fizz (Node, async)',718 () => renderFlightFizzNode(renderRSCNode, RSCAppAsync, ITEM_COUNT),719 ITERATIONS,720 WARMUP721 );722 printResult(flightFizzNodeAsync);723724 const fizzEdgeSync = await runBenchmark(725 'Fizz (Edge, sync)',726 () => renderFizzEdge(App, ITEM_COUNT),727 ITERATIONS,728 WARMUP729 );730 printResult(fizzEdgeSync);731732 const flightFizzEdgeSync = await runBenchmark(733 'Flight + Fizz (Edge, sync)',734 () => renderFlightFizzEdge(renderRSCEdge, RSCApp, ITEM_COUNT),735 ITERATIONS,736 WARMUP737 );738 printResult(flightFizzEdgeSync);739740 const fizzEdgeAsync = await runBenchmark(741 'Fizz (Edge, async)',742 () => renderFizzEdge(AppAsync, ITEM_COUNT),743 ITERATIONS,744 WARMUP745 );746 printResult(fizzEdgeAsync);747748 const flightFizzEdgeAsync = await runBenchmark(749 'Flight + Fizz (Edge, async)',750 () => renderFlightFizzEdge(renderRSCEdge, RSCAppAsync, ITEM_COUNT),751 ITERATIONS,752 WARMUP753 );754 printResult(flightFizzEdgeAsync);755756 const median = r => r.median;757758 console.log('\n--- Flight overhead ---\n');759 printGrid(760 ['Fizz', 'Flight+Fizz'],761 [762 ['Node sync', fizzNodeSync, flightFizzNodeSync],763 ['Node async', fizzNodeAsync, flightFizzNodeAsync],764 ['Edge sync', fizzEdgeSync, flightFizzEdgeSync],765 ['Edge async', fizzEdgeAsync, flightFizzEdgeAsync],766 ],767 median,768 'ms',769 'median, lower is better'770 );771772 console.log('\n--- Edge vs Node ---\n');773 printGrid(774 ['Node', 'Edge'],775 [776 ['Fizz sync', fizzNodeSync, fizzEdgeSync],777 ['Fizz async', fizzNodeAsync, fizzEdgeAsync],778 ['Flight+Fizz sync', flightFizzNodeSync, flightFizzEdgeSync],779 ['Flight+Fizz async', flightFizzNodeAsync, flightFizzEdgeAsync],780 ],781 median,782 'ms',783 'median, lower is better'784 );785786 writeJsonOut(INJECT ? 'inject' : 'bare');787}788789main().catch(function (err) {790 console.error(err);791 process.exit(1);792});
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.