Cache the page-owned app response before starting the worker

This commit is contained in:
Jonathan Sykes
2026-10-08 09:04:20 +08:00
parent e7a7cdc6b9
commit a86fa1cebe
16 changed files with 186 additions and 17 deletions

View File

@@ -346,3 +346,7 @@ Every cold sample has `waterfalls`: proxy request order/start/headers/end times
and page Resource Timing discovery/start/end times. Proxy times start when
tracking begins; page times start at navigation. Compare within each clock,
then use the page FCP to locate the rendering dependency.
Pass `--assert-cold` on new builds to require exactly one positive-byte app.js
response in every cold sample, including worker installation. Omit this assertion
when diagnosing historical regressions.

View File

@@ -26,7 +26,7 @@
*/
import assert from 'node:assert/strict';
import { assertChangedAssets, assertWarmAssets, waitForIdleAssets } from './measurement.mjs';
import { assertColdAssets, assertChangedAssets, assertWarmAssets, waitForIdleAssets } from './measurement.mjs';
import { createHash } from 'node:crypto';
import { spawn, spawnSync, execFileSync } from 'node:child_process';
import { existsSync, mkdirSync, rmSync, cpSync, appendFileSync, statSync, writeFileSync, readFileSync, symlinkSync, unlinkSync } from 'node:fs';
@@ -63,6 +63,7 @@ function parseArgs() {
compare: null,
frontendCommit: null,
sourceRoot: null,
assertCold: false,
};
for (let i = 0; i < args.length; i++) {
@@ -72,6 +73,7 @@ function parseArgs() {
else if (a === '--profile' && i + 1 < args.length) options.profile = args[++i];
else if (a === '--scenario' && i + 1 < args.length) options.scenario = args[++i];
else if (a === '--out' && i + 1 < args.length) options.out = args[++i];
else if (a === '--assert-cold') options.assertCold = true;
else if (a === '--source-root' && i + 1 < args.length) options.sourceRoot = path.resolve(args[++i]);
else if (a === '--frontend-commit' && i + 1 < args.length) options.frontendCommit = args[++i];
else if (a === '--compare' && i + 1 < args.length) options.compare = args[++i];
@@ -774,6 +776,7 @@ async function main() {
for (let r = 0; r < options.runs; r++) {
process.stdout.write(` run ${r + 1}/${options.runs}... `);
const res = await runColdScenario({ browserType: bType, server, proxy, profile: prof });
if(options.assertCold)assertColdAssets(res.requests);
coldRuns.push(res);
console.log(`done (${res.wireBytes} bytes, ${res.requestCount} reqs, boot=${res.bootDone}ms)`);
}

View File

@@ -27,3 +27,8 @@ export function assertWarmAssets(requests) {
const assets=requests.filter(r=>r.bytes>0&&/\.(?:js|css|woff2?|png|svg)(?:[?#]|$)/.test(r.url)&&!new URL(r.url,'http://localhost').pathname.endsWith('/sw.js'));
assert.deepEqual(assets,[],'warm reload does not transfer unfinished shell assets');
}
export function assertColdAssets(requests) {
const transferred=requests.filter(r=>r.bytes>0&&new URL(r.url,'http://localhost').pathname==='/app.js');
assert.equal(transferred.length,1,'cold app.js transfers its body exactly once across page and worker');
}

View File

@@ -1,6 +1,6 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { assertChangedAssets, assertWarmAssets } from './measurement.mjs';
import { assertChangedAssets, assertWarmAssets, assertColdAssets } from './measurement.mjs';
test('changed CSS and feature measurements reject unrelated application transfers',()=>{
const requests=[{url:'/sw.js',bytes:7000},{url:'/asset-sync-core.js',bytes:2000},{url:'/index.html',bytes:12000},{url:'/api/manifest',bytes:3000},{url:'/theme-glass.css?v=new',bytes:5000},{url:'/fonts/a.woff2',bytes:0}];
assertChangedAssets(requests,'/theme-glass.css');
@@ -14,3 +14,10 @@ test('warm measurements reject unfinished shell downloads but allow API checks',
assertWarmAssets([{url:'/api/version',bytes:62},{url:'/app.js?v=current',bytes:0}]);
assert.throws(()=>assertWarmAssets([{url:'/theme-glass.css?v=current',bytes:5000}]));
});
test('cold acceptance rejects a second positive app response while allowing cache revalidation',()=>{
assertColdAssets([{url:'/app.js?v=current',bytes:12000},{url:'/app.js?v=current',bytes:0}]);
assert.throws(()=>assertColdAssets([{url:'/app.js?v=current',bytes:12000},{url:'/app.js?v=current',bytes:12000}]));
assert.throws(()=>assertColdAssets([]));
});